1
+ """
2
+ Task:
3
+ Given a string and a list of words, return true if the string can be
4
+ segmented into a space-separated sequence of one or more words.
5
+
6
+ Note that the same word may be reused
7
+ multiple times in the segmentation.
8
+
9
+ Implementation notes: Trie + Dynamic programming up -> down.
10
+ The Trie will be used to store the words. It will be useful for scanning
11
+ available words for the current position in the string.
12
+
13
+ Leetcode:
14
+ https://leetcode.com/problems/word-break/description/
15
+
16
+ Runtime: O(n * n)
17
+ Space: O(n)
18
+ """
19
+
20
+ import functools
21
+ from typing import Any
22
+
23
+
24
+ def word_break (string : str , words : list [str ]) -> bool :
25
+ """
26
+ Return True if numbers have opposite signs False otherwise.
27
+
28
+ >>> word_break("applepenapple", ["apple","pen"])
29
+ True
30
+ >>> word_break("catsandog", ["cats","dog","sand","and","cat"])
31
+ False
32
+ >>> word_break("cars", ["car","ca","rs"])
33
+ True
34
+ >>> word_break('abc', [])
35
+ False
36
+ >>> word_break(123, ['a'])
37
+ Traceback (most recent call last):
38
+ ...
39
+ ValueError: the string should be not empty string
40
+ >>> word_break('', ['a'])
41
+ Traceback (most recent call last):
42
+ ...
43
+ ValueError: the string should be not empty string
44
+ >>> word_break('abc', [123])
45
+ Traceback (most recent call last):
46
+ ...
47
+ ValueError: the words should be a list of non-empty strings
48
+ >>> word_break('abc', [''])
49
+ Traceback (most recent call last):
50
+ ...
51
+ ValueError: the words should be a list of non-empty strings
52
+ """
53
+
54
+ # Validation
55
+ if not isinstance (string , str ) or len (string ) == 0 :
56
+ raise ValueError ("the string should be not empty string" )
57
+
58
+ if not isinstance (words , list ) or not all (
59
+ isinstance (item , str ) and len (item ) > 0 for item in words
60
+ ):
61
+ raise ValueError ("the words should be a list of non-empty strings" )
62
+
63
+
64
+ # Build trie
65
+ trie : dict [str , Any ] = {}
66
+ word_keeper_key = "WORD_KEEPER"
67
+
68
+ for word in words :
69
+ trie_node = trie
70
+ for c in word :
71
+ if c not in trie_node :
72
+ trie_node [c ] = {}
73
+
74
+ trie_node = trie_node [c ]
75
+
76
+ trie_node [word_keeper_key ] = True
77
+
78
+ len_string = len (string )
79
+
80
+ # Dynamic programming method
81
+ @functools .cache
82
+ def is_breakable (index : int ) -> bool :
83
+ """
84
+ >>> string = 'a'
85
+ >>> is_breakable(1)
86
+ True
87
+ """
88
+ if index == len_string :
89
+ return True
90
+
91
+ trie_node = trie
92
+ for i in range (index , len_string ):
93
+ trie_node = trie_node .get (string [i ], None )
94
+
95
+ if trie_node is None :
96
+ return False
97
+
98
+ if trie_node .get (word_keeper_key , False ) and is_breakable (i + 1 ):
99
+ return True
100
+
101
+ return False
102
+
103
+ return is_breakable (0 )
104
+
105
+
106
+ if __name__ == "__main__" :
107
+ print (word_break ("applepenapple" , ["apple" , "pen" ]))
0 commit comments