Skip to content

Commit 4b8a49b

Browse files
authored
Merge pull request #93 from myoshi2891/dev/macbook_pro
Dev/macbook pro
2 parents fbc86d6 + 9eb619c commit 4b8a49b

17 files changed

Lines changed: 2245 additions & 0 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// 以下は、指定された問題に対する **TypeScript (5.1) + Node.js 18.16.1** 向けの **DP(動的計画法)版の解法** です。クラスは使わず、**関数ベース**で記述し、**LeetCodeの効率**も意識してあります。
2+
3+
// ---
4+
5+
// ### ✅ アルゴリズム概要(DP法)
6+
7+
// * `dp[i]` を「`i` 番目の文字を終点とする最長の有効な括弧の長さ」と定義します。
8+
// * `s[i] === ')'` であり、`s[i - 1] === '('` の場合 → `dp[i] = dp[i - 2] + 2`
9+
// * `s[i] === ')'` であり、`s[i - 1] === ')'` の場合は、`s[i - dp[i - 1] - 1] === '('` を確認してペアを拡張
10+
11+
// ---
12+
13+
// ### ✅ TypeScript 実装
14+
15+
// ```typescript
16+
/**
17+
* 最長の有効な括弧部分文字列の長さを返す(DP法)
18+
*
19+
* @param s - '(' と ')' のみからなる文字列
20+
* @returns 最長の well-formed な部分文字列の長さ
21+
*/
22+
function longestValidParenthesesDP(s: string): number {
23+
const n: number = s.length;
24+
const dp: number[] = new Array(n).fill(0);
25+
let maxLength: number = 0;
26+
27+
for (let i = 1; i < n; i++) {
28+
if (s[i] === ')') {
29+
if (s[i - 1] === '(') {
30+
// 直前が '(', 対応ペアなので +2
31+
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
32+
} else if (i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] === '(') {
33+
// 直前も ')', さらにその前に対応する '(' がある
34+
dp[i] = dp[i - 1] + 2;
35+
if (i - dp[i - 1] - 2 >= 0) {
36+
dp[i] += dp[i - dp[i - 1] - 2]; // 前の連結部分も足す
37+
}
38+
}
39+
maxLength = Math.max(maxLength, dp[i]);
40+
}
41+
}
42+
43+
return maxLength;
44+
}
45+
// ```
46+
47+
// ---
48+
49+
// ### ✅ 使用例
50+
51+
// ```typescript
52+
// console.log(longestValidParentheses("(()")); // 2
53+
// console.log(longestValidParentheses(")()())")); // 4
54+
// console.log(longestValidParentheses("")); // 0
55+
// console.log(longestValidParentheses("()(()")); // 2
56+
// console.log(longestValidParentheses("()()")); // 4
57+
// ```
58+
59+
// ---
60+
61+
// ### ✅ 時間・空間計算量
62+
63+
// | 指標 | 値 |
64+
// | ------- | --------------------- |
65+
// | 時間計算量 | O(n) |
66+
// | 空間計算量 | O(n) (dp 配列使用) |
67+
// | 実行時間目安 | 約 60ms〜80ms(LeetCode) |
68+
// | メモリ使用目安 | 約 45MB〜50MB(LeetCode) |
69+
70+
// ---
71+
72+
// DP法は**スタック法よりもややメモリ使用量が多め**ですが、括弧の構造を明示的に覚えて拡張していくため、構文解析に近い用途では有効です。
73+
74+
// ご希望があれば、**空間O(1)の両方向スキャン解法**も提供できます。
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// 以下に、**Go (go 1.20.6)** による解答を示します。
2+
// この問題は `(X, Y)` に至る操作列を **逆操作(減算)で復元**し、正順で出力するものです。
3+
4+
// ---
5+
6+
// ## ✅ アルゴリズム概要(逆操作)
7+
8+
// 操作は以下の2つ:
9+
10+
// * `x ← x + y`
11+
// * `y ← x + y`
12+
13+
// 逆操作では:
14+
15+
// * `x > y` のとき:`x ← x - y`
16+
// * `y > x` のとき:`y ← y - x`
17+
18+
// ---
19+
20+
// ## ✅ Go 解答コード(型明示、関数化、コメント付き)
21+
22+
// ```go
23+
package main
24+
25+
import (
26+
"bufio"
27+
"fmt"
28+
"os"
29+
"strconv"
30+
"strings"
31+
)
32+
33+
// 型定義:1ステップの操作後の x, y を表すタプル
34+
type State struct {
35+
x int
36+
y int
37+
}
38+
39+
/**
40+
* 与えられた (X, Y) に至る操作の履歴を求める。
41+
* @param X int - 目標のxの値(1 ≤ X ≤ 10^6)
42+
* @param Y int - 目標のyの値(1 ≤ Y ≤ 10^6)
43+
* @return steps []State - (1,1) → ... → (X,Y) への操作列
44+
*/
45+
func findOperations(X int, Y int) []State {
46+
var path []State
47+
x, y := X, Y
48+
49+
for !(x == 1 && y == 1) {
50+
path = append(path, State{x, y})
51+
if x > y {
52+
x -= y
53+
} else {
54+
y -= x
55+
}
56+
}
57+
58+
// 逆順に path を並び替える(reverse)
59+
for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
60+
path[i], path[j] = path[j], path[i]
61+
}
62+
return path
63+
}
64+
65+
/**
66+
* 標準入力から X, Y を読み取り、操作回数と操作履歴を出力する。
67+
* 入力形式: "X Y"
68+
* 出力形式:
69+
* K
70+
* x1 y1
71+
* x2 y2
72+
* ...
73+
* xK yK
74+
*/
75+
func main() {
76+
scanner := bufio.NewScanner(os.Stdin)
77+
scanner.Scan()
78+
line := scanner.Text()
79+
parts := strings.Fields(line)
80+
81+
X, _ := strconv.Atoi(parts[0])
82+
Y, _ := strconv.Atoi(parts[1])
83+
84+
steps := findOperations(X, Y)
85+
86+
// 出力
87+
fmt.Println(len(steps))
88+
for _, s := range steps {
89+
fmt.Printf("%d %d\n", s.x, s.y)
90+
}
91+
}
92+
93+
// ## ✅ 入力例
94+
95+
// ```
96+
// 5 2
97+
// ```
98+
99+
// ### 出力:
100+
101+
// ```
102+
// 3
103+
// 1 2
104+
// 3 2
105+
// 5 2
106+
// ```
107+
108+
// ---
109+
110+
// ## ✅ 計算量・メモリ使用
111+
112+
// | 項目 | 内容 |
113+
// | ------ | ------------------------------ |
114+
// | 時間計算量 | `O(log(max(X, Y)))`(ユークリッド互除法) |
115+
// | 空間計算量 | `O(K)`(履歴最大でも約40ステップ程度) |
116+
// | 実行時間 | ≦ 1ms @ X,Y ≤ 10^6 |
117+
// | メモリ使用量 | 数KB 程度 |
118+
119+
// ---
120+
121+
// ## ✅ 特徴と補足
122+
123+
// * 型 `State` によって `(x, y)` ペアを安全に管理。
124+
// * 操作列を `[]State` で保持することで、明示的かつ型安全に履歴を処理。
125+
// * `reverse` は Go には標準ライブラリがないため、自前で swap しています。
126+
// * `bufio.Scanner` と `strconv.Atoi` で高速入力に対応。
127+
128+
// ---
129+
130+
// 他の入力例や、より低メモリ構成(出力をリアルタイムに構築)も希望があれば対応可能です。
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// 以下は、与えられた問題に対する **JavaScript (Node.js 18.16.1)** による解答です。
2+
3+
// この問題は、操作の履歴を出力する必要があるので、**目標の `(X, Y)` から `(1, 1)` に逆戻りする逆操作を考えると効率的**です。
4+
5+
// ---
6+
7+
// ### ✅ アルゴリズム概要(逆操作)
8+
9+
// * 操作は2種類:
10+
11+
// 1. `x ← x + y`
12+
// 2. `y ← x + y`
13+
// * 逆操作では:
14+
15+
// * `x > y` のとき、直前は `x ← x + y` なので、`x ← x - y`
16+
// * `y > x` のとき、直前は `y ← x + y` なので、`y ← y - x`
17+
// * このように `x` または `y` が 1 に到達するまで操作を戻していきます。
18+
19+
// ---
20+
21+
// ### ✅ コード(Node.js, fs 使用)
22+
23+
// ```javascript
24+
// 使用Node.js: v18.16.1
25+
const fs = require('fs');
26+
27+
/**
28+
* 与えられたX, Yを (1, 1) からの操作列で構築する手順を返す。
29+
* @param {number} X - 目標の x の値
30+
* @param {number} Y - 目標の y の値
31+
* @returns {string} - 操作回数と各操作後の (x, y) のリストを文字列で返す
32+
*/
33+
function findOperations(X, Y) {
34+
const path = [];
35+
let x = X, y = Y;
36+
37+
// 逆から構築
38+
while (!(x === 1 && y === 1)) {
39+
path.push([x, y]);
40+
if (x > y) {
41+
x -= y;
42+
} else {
43+
y -= x;
44+
}
45+
}
46+
47+
path.reverse(); // 初期状態 (1, 1) から順に並べる
48+
49+
let output = path.length + '\n';
50+
for (const [a, b] of path) {
51+
output += `${a} ${b}\n`;
52+
}
53+
return output;
54+
}
55+
56+
// ---- 入出力処理部 ----
57+
function main() {
58+
const input = fs.readFileSync('/dev/stdin', 'utf8').trim().split(/\s+/).map(Number);
59+
const [X, Y] = input;
60+
const result = findOperations(X, Y);
61+
console.log(result);
62+
}
63+
64+
main();
65+
// ```
66+
67+
// ---
68+
69+
// ### ✅ 実行例
70+
71+
// #### 入力:
72+
73+
// ```
74+
// 5 2
75+
// ```
76+
77+
// #### 出力:
78+
79+
// ```
80+
// 3
81+
// 1 2
82+
// 3 2
83+
// 5 2
84+
// ```
85+
86+
// ---
87+
88+
// ### ✅ 計算量・メモリ
89+
90+
// * 時間計算量: `O(log(max(X, Y)))`(ユークリッドの互除法に類似)
91+
// * 空間計算量: `O(K)` (操作のステップ数に比例)
92+
93+
// ---
94+
95+
// ### ✅ 注意点
96+
97+
// * 入力 `(X, Y)` の最大公約数は 1 なので、確実に `(1, 1)` に到達可能。
98+
// * 各ステップで操作内容を記録し、最終的に逆順に出力すればOK。

0 commit comments

Comments
 (0)