Word Break LeetCode 139
Given a non-empty string s and a dictionary wordDict containing a list of non-emptywords, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
- The same word in the dictionary may be reused multiple times in the segmentation.
- You may assume the dictionary does not contain duplicate words.
Example 1:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
分析:看题目就像动态规划,但却无从下手,因为感觉怎么做都好像复杂度很高的样子。实际上它的复杂度就是要至少O(n^2)。
这里采用动态规划,对应一维dp数组含义是:[0,i)的字符串是否可以分解为字典中的单词,dp长度要比s长度多1,因为在解dp的时候要考虑空字符串的情况。
下面代码中,第一个循环是遍历字符串,第二个循环是对[0,i)的字符串进行切分,然后判断[0 : j ) 和 [ j : i )这两部分是否在字典中有效,前一部分已经计算过,直接在dp中查找,后一部分则在字典中查找,因为只要找到一种可行的切分解就行了,所以及时break掉。
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
wordSet = set(wordDict) # 变成set可以使查找复杂度降为O(1)
dp = [False for i in range(len(s)+1)]
dp[0] = True
for i in range(1,len(s)+1):
for j in range(i):
if dp[j] and s[j:i] in wordSet:
dp[i]=True
break
return dp[-1]
参考自:https://www.cnblogs.com/grandyang/p/4257740.html
Word Break II LeetCode 140
Given a non-empty string s and a dictionary wordDict containing a list of non-emptywords, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.
Note:
- The same word in the dictionary may be reused multiple times in the segmentation.
- You may assume the dictionary does not contain duplicate words.
Example 1:
Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
"cats and dog",
"cat sand dog"
]
分析:这是上一题的升级版,需要给出所有可能的分词情况,上一题中,获得一种分词情况后就会break,这里就需要遍历完所有可能情况,并把它们记录下来,所以dp数组需要通过列表记录下可分解的所有情况 。
之后的helper函数,是要找出所有的路径,这里用的DFS方法,从dp[-1]不断向前查找,该函数中的最后三行非常经典,记住他。
class Solution:
def wordBreak(self, s, wordDict):
wordSet = set(wordDict)
dp = [[] for i in range(len(s)+1)]
dp[0] = 1 # 一个非空常数即可
res = []
temp = [len(s)]
for i in range(1,len(s)+1):
for j in range(i):
if dp[j] and s[j:i] in wordSet:
dp[i].append(j)
# 上面部分实际上是和上一题一样的,只是少了break, 然后dp中用数组去存储所有可能的分词位置
# print(dp) [1, [], [], [0], [0], [], [], [3, 4], [], [], [7]]
# 所给示例下得到的dp矩阵如上,再通过dp[-1]不断向上回溯找到所有可能解
if dp[-1]:
self.helper(res, s, temp, dp, -1)
return res
def helper(self, res, s, temp, dp, m):
if m==0:
str_res = ""
for i in range(len(temp)-1, 0, -1):
str_res += s[temp[i]:temp[i-1]]
str_res += " "
res.append(str_res[:-1])
else:
for j in dp[m]:
temp.append(j)
self.helper(res, s, temp, dp, j)
temp.pop()
# 以上三行在回溯法,DFS中都会经常使用。
# 这样理解,不管中间的递归发生了什么,先append再pop,temp回到原样进行下一次的循环
sol=Solution()
s="catsanddog"
wordDict = ["cat","cats","and","sand","dog"]
res = sol.wordBreak(s,wordDict)
print(res)