Skip to content

Commit 5f0c9ff

Browse files
committed
solve Word Break
1 parent 242a1f3 commit 5f0c9ff

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed

problems/word_break.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from typing import List
2+
3+
4+
class OfficialSolution:
5+
def wordBreakApproach1(self, s: str, wordDict: List[str]) -> bool:
6+
"""
7+
== Approach 1: Brute Force ==
8+
== Algorithm ==
9+
The naive approach to solve this problem is to use recursion and backtracking.
10+
For finding the solution, we check every possible prefix of that string in the
11+
dictionary of words. If it is found in the dictionary, then the recursive
12+
function is called for the remaining portion of that string. And, if in some
13+
function call it is found that the complete string is in dictionary, then it
14+
will return true.
15+
16+
== Complexity Analysis ==
17+
- Time Complexity: O(n^n). Consider the worst case where s = "aaaaaaa" and every
18+
prefix of s is present in the dictionary of words, then the recursion tree
19+
can grow upto n^n.
20+
- Space Complexity: O(n). The depth of the recursion tree can go upto n.
21+
"""
22+
n = len(s)
23+
24+
def backtrack(st=0):
25+
if st > n:
26+
return True
27+
28+
for i in range(st + 1, n + 2):
29+
if s[st:i] in wordDict:
30+
if backtrack(st=i) is True:
31+
return True
32+
33+
return False
34+
35+
return backtrack()
36+
37+
def wordBreakApproach2(self, s: str, wordDict: List[str]) -> bool:
38+
"""
39+
== Approach 2: Recursion with memoization ==
40+
== Algorithm ==
41+
In the previous approach, we can see that many subproblems were redundant, i.e.
42+
we were calling the recursive function multiple times for a particular string.
43+
To avoid this we can use memoization method, where an array memo is used to
44+
store the result of the subproblems. Now, when the function is called again for
45+
a particular string, value will be fetches and returned using the memo array, if
46+
its value has been already evaluated.
47+
48+
With memoization many redundant subproblems are avoided and recursion tree is
49+
pruned and thus it reduces the time complexity by a large factor.
50+
51+
== Complexity Analysis ==
52+
- Time Complexity: O(n^2). Size of the recursion tree can go up to n^2.
53+
- Space Complexity: O(n). The depth of recursion tree can go up to n.
54+
"""
55+
n = len(s)
56+
memo = [None] * (n + 2)
57+
58+
def backtrack(st=0):
59+
if memo[st] is not None:
60+
return memo[st]
61+
62+
if st > n:
63+
memo[st] = True
64+
return True
65+
66+
for i in range(st + 1, n + 2):
67+
if s[st:i] in wordDict:
68+
if backtrack(st=i) is True:
69+
memo[st] = True
70+
return True
71+
72+
memo[st] = False
73+
return False
74+
75+
return backtrack()

tests/test_word_break.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import unittest
2+
3+
from word_break import OfficialSolution
4+
5+
6+
class TestWordBreak(unittest.TestCase):
7+
def test_example_1(self):
8+
assert (
9+
OfficialSolution().wordBreakApproach1(
10+
s="leetcode", wordDict=["leet", "code"]
11+
)
12+
is True
13+
)
14+
assert (
15+
OfficialSolution().wordBreakApproach2(
16+
s="leetcode", wordDict=["leet", "code"]
17+
)
18+
is True
19+
)
20+
21+
def test_example_2(self):
22+
assert (
23+
OfficialSolution().wordBreakApproach1(
24+
s="applepenapple", wordDict=["apple", "pen"]
25+
)
26+
is True
27+
)
28+
assert (
29+
OfficialSolution().wordBreakApproach2(
30+
s="applepenapple", wordDict=["apple", "pen"]
31+
)
32+
is True
33+
)
34+
35+
def test_example_3(self):
36+
assert (
37+
OfficialSolution().wordBreakApproach1(
38+
s="catsandog", wordDict=["cats", "dog", "sand", "and", "cat"]
39+
)
40+
is False
41+
)
42+
assert (
43+
OfficialSolution().wordBreakApproach2(
44+
s="catsandog", wordDict=["cats", "dog", "sand", "and", "cat"]
45+
)
46+
is False
47+
)

0 commit comments

Comments
 (0)