forked from TheAlgorithms/Python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathword_break.py
105 lines (82 loc) · 3.02 KB
/
word_break.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
"""
Author : Alexander Pantyukhin
Date : December 12, 2022
Task:
Given a string s and a dictionary of strings wordDict,
return true if s can be segmented into a space-separated
sequence of one or more dictionary words.
Note that the same word in the dictionary may be reused
multiple times in the segmentation.
Implementation notes: Trie + Dynamic programming up -> down.
The Trie keeps all wordDict words. It will be useful for scanning
available words for the current position in the string.
Leetcode:
https://leetcode.com/problems/word-break/description/
Runtime: O(n * n)
Space: O(n)
"""
from functools import lru_cache
def word_break(string: str, word_dict: list[str]) -> bool:
"""
Return True if numbers have opposite signs False otherwise.
>>> word_break("applepenapple", ["apple","pen"])
True
>>> word_break("catsandog", ["cats","dog","sand","and","cat"])
False
>>> word_break("cars", ["car","ca","rs"])
True
>>> word_break("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"])
False
>>> word_break('abc', [])
False
>>> word_break(123, ['a'])
Traceback (most recent call last):
...
ValueError: the string should be not empty string
>>> word_break('', ['a'])
Traceback (most recent call last):
...
ValueError: the string should be not empty string
>>> word_break('abc', [123])
Traceback (most recent call last):
...
ValueError: the word_dict should a list of non empty string
>>> word_break('abc', [''])
Traceback (most recent call last):
...
ValueError: the word_dict should a list of non empty string
"""
# Validation
if not isinstance(string, str) or len(string) == 0:
raise ValueError('the string should be not empty string')
if not isinstance(word_dict, list) or not all(
[isinstance(item, str) and len(item) > 0 for item in word_dict]):
raise ValueError('the word_dict should a list of non empty string')
# Build trie
trie = {}
WORD_KEEPER = 'WORD_KEEPER'
for word in word_dict:
trie_node = trie
for c in word:
if c not in trie_node:
trie_node[c] = {}
trie_node = trie_node[c]
trie_node[WORD_KEEPER] = True
len_string = len(string)
# Dynamic programming method
@lru_cache(maxsize=None)
def is_breakable(index: int) -> bool:
if index == len_string:
return True
trie_node = trie
for i in range(index, len_string):
trie_node = trie_node.get(string[i], None)
if trie_node is None:
return False
if trie_node.get(WORD_KEEPER, False) and is_breakable(i + 1):
return True
return False
return is_breakable(0)
if __name__ == "__main__":
import doctest
doctest.testmod()