-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlc_0140_word_break_original.py
115 lines (86 loc) Β· 3.24 KB
/
lc_0140_word_break_original.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""140. Word Break II
Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences in any order.
Note that the same word in the dictionary may be reused multiple times in the segmentation.
---
Writing: 30 minutes
Debugging: 5 minutes
Writing score: πππππππ’π€¬π€¬
Runtime: 32 ms, faster than 65.92% of Python3 online submissions for Word Break II.
Memory Usage: 14.7 MB, less than 9.42% of Python3 online submissions for Word Break II.
"""
from collections import defaultdict
from functools import lru_cache
# π The order of imports was not alphabetic
from typing import List
memoize = lru_cache(None)
# π This should have been two empty lines according to PEP 8
def factory():
return defaultdict(factory)
# π This should have been two empty lines according to PEP 8
class Trie:
def __init__(self, word_list=None):
self.data = factory()
if word_list:
for w in word_list:
self.add(w)
def find_prefix(self, prefix, create=False):
d = self.data
for c in prefix:
if c in d or create:
d = d[c]
else:
return None
return d
def add(self, word):
self.find_prefix(word, create=True)[None] = True
def __contains__(self, word):
if not isinstance(word, str):
return False
d = self.find_prefix(word)
return (None in d) if d else False
def has_prefix(self, prefix):
return self.find_prefix(prefix) is not None
def walk(self, s, from_index):
"""Yields all values of index for which s[from_index:index] is in the trie"""
d = self.data
for i in range(from_index, len(s)):
if None in d:
yield i
c = s[i]
if c not in d:
return
d = d[c]
if None in d:
yield len(s)
t = Trie(["ab", "abc"])
t.add("abba")
assert "ab" in t
assert "abba" in t
assert "a" not in t
assert t.has_prefix("a")
assert t.has_prefix("abba")
assert list(t.walk("jabba", 1)) == [3, 5]
# π This should have been two empty lines according to PEP 8
# π’ The function + assertion together had an off-by-one bug
def add_spaces(s, a):
# β
This is fine -- I've decided to change the precondition
assert a[-1] == len(s)
return " ".join(s[p:c] for c, p in zip(a, [0] + a))
# π This should have been two empty lines according to PEP 8
assert add_spaces("catsanddogs", [4, 7, 11]) == "cats and dogs"
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
words = Trie(wordDict)
@memoize
def answer(from_ix):
if from_ix == len(s):
# π€¬ This was a bug, we meant "there is one choice"
return [[]]
# π This should have been an empty line
def gen():
for ix in words.walk(s, from_ix):
for right in answer(ix):
yield [ix] + right
return list(gen())
# π€¬ This was a bug, the line as written made no sense
return [add_spaces(s, a) for a in answer(0)]