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.
Example 1:
Input: s = “leetcode”, wordDict = [“leet”,”code”]
Output: true
Explanation: Return true because “leetcode” can be segmented as “leet code”.
Example 2:
Input: s = “applepenapple”, wordDict = [“apple”,”pen”]
Output: true
Explanation: Return true because “applepenapple” can be segmented as “apple pen apple”.
Note that you are allowed to reuse a dictionary word.
Example 3:
Input: s = “catsandog”, wordDict = [“cats”,”dog”,”sand”,”and”,”cat”]
Output: false
Constraints:
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s and wordDict[i] consist of only lowercase English letters.
All the strings of wordDict are unique
https://leetcode.com/problems/word-break/description/
from collections import defaultdict
class Solution(object):
def wordBreak(self, s, wordDict):
“””
:type s: str
:type wordDict: List[str]
:rtype: bool
“””
Trie = lambda: defaultdict(Trie)
trie = Trie()
for word in wordDict:
cur = trie
for c in word:
cur = cur[c]
cur[”#”] = True
dp = [False] * len(s)
for i in range(len(s)):
cur = trie
if i == 0 or dp[i-1]:
for j in range(i, len(s)):
c = s[j]
if c not in cur:
break
cur = cur[c]
if cur[”#”]:
dp[j] = True
return dp[-1]
less efficient solution
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
n = len(s)
words = set(wordDict)
dp = [False] * (n + 1)
dp[0] = True
for i in range(1, n + 1):
for j in range(i):
if dp[j] and s[j:i] in words:
dp[i] = True
break
return dp[-1]Note that the same word in the dictionary may be reused multiple times in the segmentation.
Example 1:
Input: s = “catsanddog”, wordDict = [“cat”,”cats”,”and”,”sand”,”dog”]
Output: [“cats and dog”,”cat sand dog”]
Example 2:
Input: s = “pineapplepenapple”, wordDict = [“apple”,”pen”,”applepen”,”pine”,”pineapple”]
Output: [“pine apple pen apple”,”pineapple pen apple”,”pine applepen apple”]
Explanation: Note that you are allowed to reuse a dictionary word.
Example 3:
Input: s = “catsandog”, wordDict = [“cats”,”dog”,”sand”,”and”,”cat”]
Output: []
Constraints:
1 <= s.length <= 20
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 10
s and wordDict[i] consist of only lowercase English letters.
All the strings of wordDict are unique.
Input is generated in a way that the length of the answer doesn’t exceed 105.
https://leetcode.com/problems/word-break-ii/description/
from collections import defaultdict
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: List[str]
"""
Trie = lambda: defaultdict(Trie)
trie = Trie()
for word in wordDict:
cur = trie
for c in word:
cur = cur[c]
cur["#"] = word
ret = []
seen = []
def rec(subs, words):
if not subs:
ret.append(" ".join(words))
return
retwords = words
cur = trie
for j in range(len(subs)):
c = subs[j]
if c not in cur :
return
cur = cur[c]
if "#" in cur:
rec(subs[j+1:], retwords + [cur["#"]])
rec(s, [])
return ret
Trie + Caching
from functools import cache, reduce
from collections import defaultdict
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
maxLen = reduce(lambda mlen,x: max(len(x), mlen), wordDict, 0)
wordDict = set(wordDict)
Trie = lambda: defaultdict(Trie)
trie = Trie()
for word in wordDict:
cur = trie
for c in word:
cur = cur[c]
cur["$"] = word
@cache
def rec(i):
if i == len(s):
return -1
out = []
cur = trie
for j in range(i, len(s)):
if s[j] not in cur:
return out
cur = cur[s[j]]
if "$" in cur:
res = rec(j+1)
if res == -1:
out.append(cur['$'])
else:
for part in res:
out.append(" ".join([cur['$'], part]))
return out
return rec(0)O(n2^n) worst case, 2^n worst case
A concatenated word is defined as a string that is comprised entirely of at least two shorter words (not necessarily distinct) in the given array.
Example 1:
Input: words = [“cat”,”cats”,”catsdogcats”,”dog”,”dogcatsdog”,”hippopotamuses”,”rat”,”ratcatdogcat”]
Output: [“catsdogcats”,”dogcatsdog”,”ratcatdogcat”]
Explanation: “catsdogcats” can be concatenated by “cats”, “dog” and “cats”;
“dogcatsdog” can be concatenated by “dog”, “cats” and “dog”;
“ratcatdogcat” can be concatenated by “rat”, “cat”, “dog” and “cat”.
Example 2:
Input: words = [“cat”,”dog”,”catdog”]
Output: [“catdog”]
Constraints:
1 <= words.length <= 104
1 <= words[i].length <= 30
words[i] consists of only lowercase English letters.
All the strings of words are unique.
1 <= sum(words[i].length) <= 105
https://leetcode.com/problems/concatenated-words/description/
Using Trie: Sort the words, add them after checking if they exist in Trie, combination words will have all the words they are comprised of in the Trie by the time we reach to them.
O(2^n)
from collections import defaultdict
class Solution(object):
def findAllConcatenatedWordsInADict(self, words):
"""
:type words: List[str]
:rtype: List[str]
"""
Trie = lambda: defaultdict(Trie)
trie = Trie()
ret = []
wordsSorted = sorted(words, key=len)
def wordbreak(word, wordTrie):
dp = [False] * len(word)
for i in range(len(word)):
cur = wordTrie
if i == 0 or dp[i-1]:
for j in range(i, len(word)):
c = word[j]
if c not in cur:
break
cur = cur[c]
if "#" in cur:
dp[j] = True
return dp[-1]
for word in wordsSorted:
cur = trie
if wordbreak(word, trie):
ret.append(word)
for c in word:
cur = cur[c]
cur["#"] = True
return retUsing Cache/Memo ( sort is optimization but not needed)
from functools import cache
class Solution:
def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]:
wordDict = set()
words = sorted(words, key=len)
@cache
def rec(i, word):
if i == len(word):
return 0
if word[i:] in wordDict:
return 1
sub_words = float('-inf')
for j in range(i+1, len(word)+1):
if word[i:j] in wordDict:
sub_words = max(sub_words, 1+rec(j, word))
return sub_words
output = []
for word in words:
sub_words = rec(0, word)
wordDict.add(word)
if sub_words > 1:
output.append(word)
return outputO(2^n), O(2^n)