diff --git a/arai60/word_break/phase1.py b/arai60/word_break/phase1.py new file mode 100644 index 0000000..d16474f --- /dev/null +++ b/arai60/word_break/phase1.py @@ -0,0 +1,56 @@ +# 配列を使ったdp +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + is_word_break = [False for _ in range(len(s))] + # is_word_break means s[:i+1] can be broken. + for i, c in enumerate(s): + string_from_beginning = s[:i+1] + for word in wordDict: + if string_from_beginning == word: + is_word_break[i] = True + elif i+1-len(word) >= 0 and s[i+1-len(word):i+1] == word and is_word_break[i-len(word)]: + is_word_break[i] = True + return is_word_break[len(s)-1] + +# 再起的なdp +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + is_word_break = [False for _ in range(len(s))] + seen = [False for _ in range(len(s))] + def recursiveWordBreak(index, seen, is_word_break): + if index >= len(s): + return + if seen[index]: + return + string_from_beginning = s[:index+1] + for word in wordDict: + if string_from_beginning == word: + seen[index] = True + is_word_break[index] = True + return recursiveWordBreak(index+1, seen, is_word_break) + elif index+1-len(word) >= 0 and word == s[index+1-len(word):index+1] and is_word_break[index-len(word)]: + seen[index] = True + is_word_break[index] = True + return recursiveWordBreak(index+1, seen, is_word_break) + seen[index] = True + return recursiveWordBreak(index+1, seen, is_word_break) + recursiveWordBreak(0, seen, is_word_break) + return is_word_break[len(s)-1] + +# タイムアウトしたコード, 本質的にはメモしてない再帰のように考える探索木が爆発してしまったと考えられる。 +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + char_to_string = defaultdict(list) + for string in wordDict: + char_to_string[string[0]].append(string) + stack = [0] + while stack: + cursor = stack.pop() + if cursor >= len(s): + return True + + beginning_char = s[cursor] + for string in char_to_string[beginning_char]: + if s[cursor:cursor+len(string)] in char_to_string[beginning_char]: + stack.append(cursor+len(string)) + return False diff --git a/arai60/word_break/phase2.py b/arai60/word_break/phase2.py new file mode 100644 index 0000000..adc3497 --- /dev/null +++ b/arai60/word_break/phase2.py @@ -0,0 +1,43 @@ +""" +Reference +shining-aiさん: https://github.com/shining-ai/leetcode/pull/39/files +自分も書いていてs[index+1-len(word):index+1]は時間計算量的にどうなのか気になった。ローリングハッシュで書き換えることを検討する。今回の問題はtop-downの方が書きやすかった...? +hayashi-ayさん: https://github.com/hayashi-ay/leetcode/pull/61/files +Exzrgさん: https://github.com/Exzrgs/LeetCode/pull/10/files startwithという方法もpythonにあることを学んだ。 + +結局最速はwordDict, sのローリングハッシュを計算してstoreしておくことになりそう。 +""" + +# @cacheで覚えておいて再帰 参考: shining-aiさん +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + @cache + def is_segmented(start): + if start == len(s): + return True + for word in wordDict: + if s[start:start+len(word)] != word: + continue + elif is_segmented(start+len(word)): + return True + return False + return is_segmented(0) + +# @cacheを使わずにメモ化 +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + seen = [False for _ in range(len(s))] + is_tokenized = [False for _ in range(len(s))] + def check_tokenizable(start, seen, is_tokenized): + if start == len(s): + return + if seen[start]: + return + seen[start] = True + for word in wordDict: + if s.startswith(word, start): + is_tokenized[start+len(word)-1] = True + check_tokenizable(start+len(word), seen, is_tokenized) + return + check_tokenizable(0, seen, is_tokenized) + return is_tokenized[-1] diff --git a/arai60/word_break/phase3.py b/arai60/word_break/phase3.py new file mode 100644 index 0000000..21de314 --- /dev/null +++ b/arai60/word_break/phase3.py @@ -0,0 +1,17 @@ +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + seen = [False for _ in range(len(s))] + is_tokanizable = [False for _ in range(len(s))] + def check_tokanizable(start, seen, is_tokanizable): + if start >= len(s): + return + if seen[start]: + return + seen[start] = True + for word in wordDict: + if s.startswith(word, start): + is_tokanizable[start+len(word)-1] = True + check_tokanizable(start+len(word), seen, is_tokanizable) + return + check_tokanizable(0, seen, is_tokanizable) + return is_tokanizable[-1] diff --git a/arai60/word_break/phase4.py b/arai60/word_break/phase4.py new file mode 100644 index 0000000..2d9ebd2 --- /dev/null +++ b/arai60/word_break/phase4.py @@ -0,0 +1,17 @@ +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + seen = [False] * len(s) + is_tokenizable = [False] * len(s) + def check_tokenizable(start): + if start >= len(s): + return + if seen[start]: + return + seen[start] = True + for word in wordDict: + if s.startswith(word, start): + is_tokenizable[start+len(word)-1] = True + check_tokenizable(start+len(word)) + return + check_tokenizable(0) + return is_tokenizable[-1] \ No newline at end of file