LeetCode之Word Break-动态规划python解法

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)
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页