Skip to content

Commit c6223c7

Browse files
alexpantyukhingithub-actionspre-commit-ci[bot]CaedenPHcclauss
authored
add word_break dynamic approach up -> down. (#8039)
* add word_break dynamic approach up -> down. * updating DIRECTORY.md * Update word_break.py fix review notes. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update word_break.py fix review notes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix review notes * add trie type * Update word_break.py add typing Any to trie. * Update dynamic_programming/word_break.py Co-authored-by: Caeden Perelli-Harris <[email protected]> * Update dynamic_programming/word_break.py Co-authored-by: Christian Clauss <[email protected]> * Update dynamic_programming/word_break.py Co-authored-by: Christian Clauss <[email protected]> * Update dynamic_programming/word_break.py Co-authored-by: Christian Clauss <[email protected]> * Update dynamic_programming/word_break.py Co-authored-by: Christian Clauss <[email protected]> * fix review notes Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Caeden Perelli-Harris <[email protected]> Co-authored-by: Christian Clauss <[email protected]>
1 parent b72d068 commit c6223c7

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

Diff for: DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@
328328
* [Subset Generation](dynamic_programming/subset_generation.py)
329329
* [Sum Of Subset](dynamic_programming/sum_of_subset.py)
330330
* [Viterbi](dynamic_programming/viterbi.py)
331+
* [Word Break](dynamic_programming/word_break.py)
331332

332333
## Electronics
333334
* [Builtin Voltage](electronics/builtin_voltage.py)

Diff for: dynamic_programming/word_break.py

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

0 commit comments

Comments
 (0)