Skip to content

Commit c5d9f64

Browse files
authored
Merge pull request #71 from myoshi2891/dev/macbook_pro
Dev/macbook pro
2 parents c540853 + 2d46862 commit c5d9f64

9 files changed

Lines changed: 1181 additions & 0 deletions

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
**JavaScript + DP解法** を用いて処理し、**各処理を図を使って具体的に説明**します。
2+
3+
---
4+
5+
## 🎯 問題概要(再掲)
6+
7+
* `s`:マッチさせたい文字列(例: `"aa"`
8+
* `p`:正規表現パターン(例: `"a*"`
9+
10+
**正規表現ルール**
11+
12+
| 記号 | 意味 |
13+
| --- | ----------------- |
14+
| `.` | 任意の1文字にマッチ |
15+
| `*` | 直前の文字の 0 回以上の繰り返し |
16+
17+
---
18+
19+
## 🧠 解法方針:動的計画法(DP)
20+
21+
### 🧩 状態定義
22+
23+
* `dp[i][j] = true`
24+
`s[0..i-1]`(長さ i の prefix)と `p[0..j-1]`(長さ j の prefix)がマッチする
25+
26+
---
27+
28+
## 🧮 例:`s = "aa"``p = "a*"`
29+
30+
### 初期状態
31+
32+
```
33+
s = " a a"
34+
↑ ↑
35+
i= 1 2
36+
37+
p = " a *"
38+
↑ ↑
39+
j= 1 2
40+
```
41+
42+
### DP テーブルの初期化
43+
44+
| | "" | a | \* |
45+
| -- | -- | - | -- |
46+
| "" | T | F | T |
47+
| a | F | T | T |
48+
| a | F | F | T |
49+
50+
---
51+
52+
### 📊 ステップ 1:`dp[0][0] = true`
53+
54+
空文字列同士は常にマッチ。
55+
56+
```
57+
dp[0][0] = true
58+
```
59+
60+
---
61+
62+
### 📊 ステップ 2:パターンに `*` がある場合の初期化
63+
64+
* `dp[0][2] = dp[0][0] = true``a*` が空文字列にマッチ)
65+
66+
```
67+
Pattern: "a*"
68+
→ '*' は前の 'a' を0回とみなせるのでOK
69+
```
70+
71+
---
72+
73+
### 📊 ステップ 3:DPテーブルの構築(再帰的に状態更新)
74+
75+
```js
76+
for (let i = 1; i <= m; i++) {
77+
for (let j = 1; j <= n; j++) {
78+
...
79+
}
80+
}
81+
```
82+
83+
### 更新規則(図示)
84+
85+
#### 🔹 case1: 文字一致 or `.`(1文字マッチ)
86+
87+
```
88+
if (p[j-1] === s[i-1] || p[j-1] === '.')
89+
→ dp[i][j] = dp[i-1][j-1]
90+
```
91+
92+
```
93+
s: ... a
94+
p: ... a
95+
↑ ↑
96+
i j
97+
```
98+
99+
---
100+
101+
#### 🔹 case2: `*` の場合(0回 or 1回以上)
102+
103+
```
104+
if (p[j-1] === '*') {
105+
dp[i][j] = dp[i][j-2] // * を0回使用
106+
if (p[j-2] === s[i-1] || p[j-2] === '.')
107+
dp[i][j] |= dp[i-1][j] // * を1回以上使用
108+
}
109+
```
110+
111+
```
112+
Pattern: a*
113+
↑↑
114+
j-2 j-1
115+
116+
s: a a
117+
118+
i-1
119+
```
120+
121+
---
122+
123+
## ✅ 最終的なDPテーブル(例:s="aa", p="a\*")
124+
125+
| | "" | a | \* |
126+
| -- | -- | - | -- |
127+
| "" ||||
128+
| a ||||
129+
| a ||||
130+
131+
`dp[2][2] = true`(完全一致)
132+
133+
---
134+
135+
## 🖼️ 図による処理まとめ
136+
137+
```
138+
s = "aa"
139+
p = "a*"
140+
141+
1. dp[0][0] = true // 空文字列同士マッチ
142+
143+
2. dp[0][2] = true // a* → *は0回で空文字にマッチ
144+
145+
3. dp[1][1] = true // s[0] = a, p[0] = a
146+
147+
4. dp[1][2] = true // '*' で1回以上の a
148+
149+
5. dp[2][2] = true // '*' でさらに1回
150+
```
151+
152+
---
153+
154+
## 📌 結果
155+
156+
```js
157+
console.log(dp[s.length][p.length]); // true
158+
```
159+
160+
---
161+
162+
## 🔁 応用例
163+
164+
### s = "ab", p = ".\*"
165+
166+
* `.` → 任意の文字
167+
* `*` → 何回でも繰り返し → すべての文字列にマッチ
168+
169+
```
170+
dp[2][2] = true
171+
```
172+
173+
---
174+
175+
## 🧠 メモリ&計算量
176+
177+
* 時間:O(m × n)
178+
* 空間:O(m × n)
179+
180+
* 制約が 20 以下なので問題なし(最大 400 要素)
181+
182+
---
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# 以下は、LeetCodeの「正規表現マッチング」問題に対する **Python(CPython 3.11.4対応)** の解答です。
2+
# **型アノテーション付き**で記述し、**計算量**と**メモリ使用**にも配慮しています。
3+
4+
# ## ✅ 問題概要(再掲)
5+
6+
# * `.` は任意の1文字
7+
# * `*` は直前の文字の 0 回以上の繰り返し
8+
# * `s` 全体と `p` 全体がマッチするかを判定
9+
10+
# ---
11+
12+
# ## 🧠 解法方針:動的計画法(DP)
13+
14+
# * 状態:`dp[i][j]` ⇒ `s[:i]` と `p[:j]` がマッチするか
15+
# * 遷移:
16+
17+
# * `p[j - 1] == '*'`:
18+
19+
# * `dp[i][j] = dp[i][j - 2] or (dp[i - 1][j] if p[j - 2] matches s[i - 1])`
20+
# * それ以外:
21+
22+
# * `dp[i][j] = dp[i - 1][j - 1]` if `p[j - 1] == s[i - 1]` or `p[j - 1] == '.'`
23+
24+
# ## 🐍 Python実装(型付き)
25+
26+
from typing import List
27+
28+
class Solution:
29+
def isMatch(self, s: str, p: str) -> bool:
30+
m: int = len(s)
31+
n: int = len(p)
32+
33+
# dp[i][j] := s[:i] と p[:j] がマッチするか
34+
dp: List[List[bool]] = [[False] * (n + 1) for _ in range(m + 1)]
35+
dp[0][0] = True # 空文字同士はマッチ
36+
37+
# パターン初期化(空文字にマッチする * の処理)
38+
for j in range(2, n + 1):
39+
if p[j - 1] == '*':
40+
dp[0][j] = dp[0][j - 2]
41+
42+
# DP更新
43+
for i in range(1, m + 1):
44+
for j in range(1, n + 1):
45+
if p[j - 1] == '.' or p[j - 1] == s[i - 1]:
46+
dp[i][j] = dp[i - 1][j - 1]
47+
elif p[j - 1] == '*':
48+
dp[i][j] = dp[i][j - 2] # * を 0 回使う
49+
if p[j - 2] == '.' or p[j - 2] == s[i - 1]:
50+
dp[i][j] |= dp[i - 1][j] # * を 1 回以上使う
51+
52+
return dp[m][n]
53+
54+
# ## 🔎 使用例
55+
56+
# ```python
57+
# if __name__ == "__main__":
58+
# sol = Solution()
59+
# print(sol.isMatch("aa", "a")) # False
60+
# print(sol.isMatch("aa", "a*")) # True
61+
# print(sol.isMatch("ab", ".*")) # True
62+
# ```
63+
64+
# ---
65+
66+
# ## ⏱ 計算量・メモリ消費
67+
68+
# | 項目 | 値 |
69+
# | --------- | ----------------------- |
70+
# | 時間計算量 | O(m × n) |
71+
# | 空間計算量 | O(m × n) |
72+
# | 入力サイズ最大 | 20文字(s, p) |
73+
# | 最大DP表サイズ | 21 × 21 = 441セル |
74+
# | メモリ消費(概算) | 約 441 × 1バイト ≈ **500B** |
75+
76+
# ## ✅ 補足:なぜDPか?
77+
78+
# * 再帰+メモ化でも解けるが、LeetCodeではTLEになりやすい。
79+
# * 全探索で部分一致を記録できるDPが最も安定した手法。
80+
81+
# ご希望があれば、再帰+メモ化(`lru_cache`)版や、図解を含めたステップバイステップ解析も可能です。
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// 以下は、LeetCodeの正規表現マッチング問題に対する **JavaScript(Node.js)での解答例** です。`fs` モジュールを用いて標準入力から `s` と `p` を受け取り、動的計画法(DP)で `.` や `*` を考慮した完全一致の判定を行います。
2+
3+
// ---
4+
5+
// ### ✅ 解法方針(動的計画法・2次元DP)
6+
7+
// * `dp[i][j]`:`s[0..i-1]` と `p[0..j-1]` がマッチするかを表す。
8+
// * 状態遷移:
9+
10+
// * `p[j - 1] === '*'` の場合:
11+
12+
// * 無視:`dp[i][j] = dp[i][j - 2]`
13+
// * 一致していて繰り返し使用:`dp[i][j] |= dp[i - 1][j]`
14+
// * `p[j - 1] === s[i - 1] || p[j - 1] === '.'` の場合:
15+
16+
// * `dp[i][j] = dp[i - 1][j - 1]`
17+
18+
// ---
19+
20+
// ### 📦 JavaScript(Node.js)実装(`fs` 使用)
21+
22+
// ```javascript
23+
const fs = require('fs');
24+
25+
// 入力読み込み
26+
const input = fs.readFileSync('/dev/stdin', 'utf8').trim().split('\n');
27+
const s = input[0]; // 入力文字列 s
28+
const p = input[1]; // パターン文字列 p
29+
30+
function isMatch(s, p) {
31+
const m = s.length;
32+
const n = p.length;
33+
34+
// 初期化:false埋め、dp[0][0] = true(空文字同士は一致)
35+
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(false));
36+
dp[0][0] = true;
37+
38+
// 空文字列 s に対するパターン p の初期化(例: a*, a*b*, a*b*c*)
39+
for (let j = 2; j <= n; j++) {
40+
if (p[j - 1] === '*') {
41+
dp[0][j] = dp[0][j - 2];
42+
}
43+
}
44+
45+
// DPテーブル更新
46+
for (let i = 1; i <= m; i++) {
47+
for (let j = 1; j <= n; j++) {
48+
if (p[j - 1] === '.' || p[j - 1] === s[i - 1]) {
49+
dp[i][j] = dp[i - 1][j - 1];
50+
} else if (p[j - 1] === '*') {
51+
dp[i][j] = dp[i][j - 2]; // * を0回使用
52+
if (p[j - 2] === '.' || p[j - 2] === s[i - 1]) {
53+
dp[i][j] = dp[i][j] || dp[i - 1][j]; // * を1回以上使用
54+
}
55+
}
56+
}
57+
}
58+
59+
return dp[m][n];
60+
}
61+
62+
// 出力
63+
console.log(isMatch(s, p));
64+
65+
// ### 🧪 入力方法(例)
66+
67+
// 以下のような入力ファイルまたは標準入力で動作します:
68+
69+
// ```
70+
// aa
71+
// a*
72+
// ```
73+
74+
// 出力:
75+
76+
// ```
77+
// true
78+
// ```
79+
80+
// ---
81+
82+
// ### ⏱ 性能(最悪計算量)
83+
84+
// * 時間計算量:O(m × n)
85+
// * 空間計算量:O(m × n)
86+
// * 制約が小さい(s, p ≤ 20)ので高速かつメモリ効率良好です。
87+
88+
// ---
89+
90+
// 必要であれば、標準入力ファイルを指定してテスト可能です。Node.js環境で実行する場合:
91+
92+
// ```bash
93+
// echo -e "ab\n.*" | node solution.js
94+
// ```

0 commit comments

Comments
 (0)