Skip to content

139. Word Break#37

Open
tom4649 wants to merge 4 commits intomainfrom
139.Word-Break
Open

139. Word Break#37
tom4649 wants to merge 4 commits intomainfrom
139.Word-Break

Conversation

@tom4649
Copy link
Copy Markdown
Owner

@tom4649 tom4649 commented Mar 29, 2026

Comment thread 139/sol1.py
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit sol1.py がTLEする方で、sol1_failed.py がパスする方に見受けられるので、ファイル名が逆かもしれません。

Comment thread 139/sol1.py Outdated
stripped_sub_strs = []
for word in wordDict:
if s.startswith(word):
stripped_sub_strs.append(s[len(word) :])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str.strip() をするのかと変数名を見て思いましたが、いわゆる strip 処理を行っていなさそうなので、単に sub_strings でいいかなと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

その通りだと思います。反映しました。

Comment thread 139/sol1_failed.py Outdated
if i == len_target:
return True
for word in wordDict:
if s.startswith(word, i) and can_break(i + len(word)):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど、部分文字列をわざわざ作らなくても startswith でいけるんですね、勉強になります 👀

Comment thread 139/memo.md
> は、
> "a" * 2 と "a" * 4 で表せないので、単純なバックトラックでは失敗するというのが予想です。

「正規表現で書けるからO(n)」はあくまでwordDictが定数のときの話だろう。ただこの考えは持っておきたい。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あくまでwordDictが定数のとき

これはどういう意味でしょうか。listの要素が固定されている、という意味であれば、関数が呼び出されたタイミングで固定されていると思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

計算量の見積もりにおいてて定数とみなされている、という意味です。
もし定数でなければ、正規表現を受理するオートマトンの構築自体の時間も考える必要があると思いました。

Comment thread 139/sol1_failed.py Outdated
@functools.cache
def can_break(i) -> bool:
if i == len_target:
return True
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的には冗長な説明変数に思いました。

Suggested change
return True
@functools.cache
def can_break(i) -> bool:
if i == len(s):

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ほぼ変わらないと思いますが、実行時間が定数倍減少すると思うので、このままにしておこうと思います。

Comment thread 139/sol1_failed.py Outdated
len_target = len(s)

@functools.cache
def can_break(i) -> bool:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

関数の中身まで読まないと i の意味が分からないので(呼び出し方で予測はできますが)、前説してあると丁寧です。

Suggested change
def can_break(i) -> bool:
def can_break(i: int) -> bool:
"""returns whether s[i:] can be broken."""

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど。たしかに引数のiが何を指しているのかわかりませんね。採用させていただきます。

Comment thread 139/sol1.py
len_target = len(s)

@functools.cache
def can_break(i) -> bool:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iが何を意味するのかをコードから推理する必要があったので,start_posあたりにすると良いかもしれません.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

みていただいた二人の目に留まったということは、良いコードではないのですね。
start_posにさせていただきます。

Comment thread 139/sol2.py Outdated
frontier = [0]
visited = {0}
while frontier:
position = frontier.pop()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これもstart_positionあるいはfirstの方が好みです.

Comment thread 139/memo.md
- n = |s|, m = len(wordDict), l = max([len(word) for word in wordDoct])とする

### sol1.py
- 時間 O(nml): can_breakはメモ化しているので高々O(n)回呼び出される、それぞれの関数内でwordDict内全ての文字列比較をするので O(ml)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

計算量から具体的な計算時間の目安を見積もると実行前にTLEに気付けるようになるかもしれません.
見積もり方はこちらが参考になります.
今回の場合ですと,
$nml \le 300 \times 1000 \times 20 = 6\times 10^7$
ですから,Pythonが 1e6 ~ 1e7 steps/sec であることを踏まえると最大で 1 ~ 10秒 程度かかる,と言う見積もりになるかと思います.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます、参考になりました。自分で見積もるくせをつけておきたいですね。

Comment thread 139/sol3.py

@dataclasses.dataclass
class TrieNode:
children: Dict[str, TrieNode] = dataclasses.field(default_factory=dict)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

別にList[TrieNode]を用意し,辞書自体はDict[str, int]としておいて,map先のintを使ってList[TrieNode]からTrieNodeを取り出すようにすると早いかもしれない,と言う議論がありました(cf).

Pythonのリストは動的配列だったはずなので,同様の議論が成り立つと思いますが,インタプリタ言語である以上ボトルネックがC++と異なるので,可読性なども考慮した上でやる価値があるかは不明です...

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど、C++の場合にはキャッシュヒットが増えそうなのは理解ができました。
Pythonで行う必要性があるのかは確かに疑問ですね。
このようなことも考えながらコードを書けるようになりたいです。

Comment thread 139/sol3.py

class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
len_target = len(s)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好みの問題でしょうが,len(s)のままで良いと思います.情報量が変わらず,長さも短くなるわけではないので.

Comment thread 139/sol3.py Outdated
root = TrieNode()
max_len_of_wordDict = 0
for word in wordDict:
max_len_of_wordDict = max(max_len_of_wordDict, len(w))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的にはmax_len_of_wordDictを求めるロジックはTrie木を作るロジックと別物であるので分離するのが好みです.
このfor文の後で,

max_len_wordDict = max(map(len, wordDict), default=0)

max_len_wordDict = max(len(word) for word in wordDict)

などとするのはいかがでしょうか.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

後の方を採用しました(しかもw と word が間違っていましたね)

Comment thread 139/memo.md

「正規表現で書けるからO(n)」はあくまでwordDictが定数のときの話だろう。ただこの考えは持っておきたい。

> というわけで、先頭から DP が"模範解答"だろうな、とは思います。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

背景として、 Vitabi のアルゴリズムがありそうな気がしました。
https://ja.wikipedia.org/wiki/%E3%83%93%E3%82%BF%E3%83%93%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最大確率を求めるわけではないのでやや疑問ですが、たしかにインデックスを状態としたDPを行うのは似ているのかもしれません

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants