Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 以下は、指定された問題に対する **TypeScript (5.1) + Node.js 18.16.1** 向けの **DP(動的計画法)版の解法** です。クラスは使わず、**関数ベース**で記述し、**LeetCodeの効率**も意識してあります。

// ---

// ### ✅ アルゴリズム概要(DP法)

// * `dp[i]` を「`i` 番目の文字を終点とする最長の有効な括弧の長さ」と定義します。
// * `s[i] === ')'` であり、`s[i - 1] === '('` の場合 → `dp[i] = dp[i - 2] + 2`
// * `s[i] === ')'` であり、`s[i - 1] === ')'` の場合は、`s[i - dp[i - 1] - 1] === '('` を確認してペアを拡張

// ---

// ### ✅ TypeScript 実装

// ```typescript
/**
* 最長の有効な括弧部分文字列の長さを返す(DP法)
*
* @param s - '(' と ')' のみからなる文字列
* @returns 最長の well-formed な部分文字列の長さ
*/
function longestValidParenthesesDP(s: string): number {
const n: number = s.length;
const dp: number[] = new Array(n).fill(0);
let maxLength: number = 0;

for (let i = 1; i < n; i++) {
if (s[i] === ')') {
if (s[i - 1] === '(') {
// 直前が '(', 対応ペアなので +2
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] === '(') {
// 直前も ')', さらにその前に対応する '(' がある
dp[i] = dp[i - 1] + 2;
if (i - dp[i - 1] - 2 >= 0) {
dp[i] += dp[i - dp[i - 1] - 2]; // 前の連結部分も足す
}
}
maxLength = Math.max(maxLength, dp[i]);
}
}

return maxLength;
}
// ```

// ---

// ### ✅ 使用例

// ```typescript
// console.log(longestValidParentheses("(()")); // 2
// console.log(longestValidParentheses(")()())")); // 4
// console.log(longestValidParentheses("")); // 0
// console.log(longestValidParentheses("()(()")); // 2
// console.log(longestValidParentheses("()()")); // 4
// ```

// ---

// ### ✅ 時間・空間計算量

// | 指標 | 値 |
// | ------- | --------------------- |
// | 時間計算量 | O(n) |
// | 空間計算量 | O(n) (dp 配列使用) |
// | 実行時間目安 | 約 60ms〜80ms(LeetCode) |
// | メモリ使用目安 | 約 45MB〜50MB(LeetCode) |

// ---

// DP法は**スタック法よりもややメモリ使用量が多め**ですが、括弧の構造を明示的に覚えて拡張していくため、構文解析に近い用途では有効です。

// ご希望があれば、**空間O(1)の両方向スキャン解法**も提供できます。
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 以下に、**Go (go 1.20.6)** による解答を示します。
// この問題は `(X, Y)` に至る操作列を **逆操作(減算)で復元**し、正順で出力するものです。

// ---

// ## ✅ アルゴリズム概要(逆操作)

// 操作は以下の2つ:

// * `x ← x + y`
// * `y ← x + y`

// 逆操作では:

// * `x > y` のとき:`x ← x - y`
// * `y > x` のとき:`y ← y - x`

// ---

// ## ✅ Go 解答コード(型明示、関数化、コメント付き)

// ```go
package main

import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)

// 型定義:1ステップの操作後の x, y を表すタプル
type State struct {
x int
y int
}

/**
* 与えられた (X, Y) に至る操作の履歴を求める。
* @param X int - 目標のxの値(1 ≤ X ≤ 10^6)
* @param Y int - 目標のyの値(1 ≤ Y ≤ 10^6)
* @return steps []State - (1,1) → ... → (X,Y) への操作列
*/
func findOperations(X int, Y int) []State {
var path []State
x, y := X, Y

for !(x == 1 && y == 1) {
path = append(path, State{x, y})
if x > y {
x -= y
} else {
y -= x
}
}

// 逆順に path を並び替える(reverse)
for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
path[i], path[j] = path[j], path[i]
}
return path
}

/**
* 標準入力から X, Y を読み取り、操作回数と操作履歴を出力する。
* 入力形式: "X Y"
* 出力形式:
* K
* x1 y1
* x2 y2
* ...
* xK yK
*/
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
line := scanner.Text()
parts := strings.Fields(line)

X, _ := strconv.Atoi(parts[0])
Y, _ := strconv.Atoi(parts[1])

steps := findOperations(X, Y)

// 出力
fmt.Println(len(steps))
for _, s := range steps {
fmt.Printf("%d %d\n", s.x, s.y)
}
}

// ## ✅ 入力例

// ```
// 5 2
// ```

// ### 出力:

// ```
// 3
// 1 2
// 3 2
// 5 2
// ```

// ---

// ## ✅ 計算量・メモリ使用

// | 項目 | 内容 |
// | ------ | ------------------------------ |
// | 時間計算量 | `O(log(max(X, Y)))`(ユークリッド互除法) |
// | 空間計算量 | `O(K)`(履歴最大でも約40ステップ程度) |
// | 実行時間 | ≦ 1ms @ X,Y ≤ 10^6 |
// | メモリ使用量 | 数KB 程度 |

// ---

// ## ✅ 特徴と補足

// * 型 `State` によって `(x, y)` ペアを安全に管理。
// * 操作列を `[]State` で保持することで、明示的かつ型安全に履歴を処理。
// * `reverse` は Go には標準ライブラリがないため、自前で swap しています。
// * `bufio.Scanner` と `strconv.Atoi` で高速入力に対応。

// ---

// 他の入力例や、より低メモリ構成(出力をリアルタイムに構築)も希望があれば対応可能です。
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 以下は、与えられた問題に対する **JavaScript (Node.js 18.16.1)** による解答です。

// この問題は、操作の履歴を出力する必要があるので、**目標の `(X, Y)` から `(1, 1)` に逆戻りする逆操作を考えると効率的**です。

// ---

// ### ✅ アルゴリズム概要(逆操作)

// * 操作は2種類:

// 1. `x ← x + y`
// 2. `y ← x + y`
// * 逆操作では:

// * `x > y` のとき、直前は `x ← x + y` なので、`x ← x - y`
// * `y > x` のとき、直前は `y ← x + y` なので、`y ← y - x`
// * このように `x` または `y` が 1 に到達するまで操作を戻していきます。

// ---

// ### ✅ コード(Node.js, fs 使用)

// ```javascript
// 使用Node.js: v18.16.1
const fs = require('fs');

/**
* 与えられたX, Yを (1, 1) からの操作列で構築する手順を返す。
* @param {number} X - 目標の x の値
* @param {number} Y - 目標の y の値
* @returns {string} - 操作回数と各操作後の (x, y) のリストを文字列で返す
*/
function findOperations(X, Y) {
const path = [];
let x = X, y = Y;

// 逆から構築
while (!(x === 1 && y === 1)) {
path.push([x, y]);
if (x > y) {
x -= y;
} else {
y -= x;
}
}

path.reverse(); // 初期状態 (1, 1) から順に並べる

let output = path.length + '\n';
for (const [a, b] of path) {
output += `${a} ${b}\n`;
}
return output;
}

// ---- 入出力処理部 ----
function main() {
const input = fs.readFileSync('/dev/stdin', 'utf8').trim().split(/\s+/).map(Number);
const [X, Y] = input;
const result = findOperations(X, Y);
console.log(result);
}

main();
// ```

// ---

// ### ✅ 実行例

// #### 入力:

// ```
// 5 2
// ```

// #### 出力:

// ```
// 3
// 1 2
// 3 2
// 5 2
// ```

// ---

// ### ✅ 計算量・メモリ

// * 時間計算量: `O(log(max(X, Y)))`(ユークリッドの互除法に類似)
// * 空間計算量: `O(K)` (操作のステップ数に比例)

// ---

// ### ✅ 注意点

// * 入力 `(X, Y)` の最大公約数は 1 なので、確実に `(1, 1)` に到達可能。
// * 各ステップで操作内容を記録し、最終的に逆順に出力すればOK。
Loading