DP专题6 - leetcode140. Word Break II/10. Regular Expression Matching -Hard

140. Word Break II

题目描述

给定一非空字符串s和一包含非空单词的wordDict。在s中添加空格构成句子,使得每个单词都在wordDict中。输出所有可能的句子。

wordDict中相同的单词可以使用多次;
假设wordDict不包含重复单词。

例子
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:
[]

思想
定义一个函数check - DP判断子字符串是否可以由wordDict中的词汇组成;
(法1 - DFS)
(法2 - DP)
存储截止s[:i], 满足题意的所有可能组合

解法1
DFS - 利用判断当前子串是否可以由wordDict中的词汇组成

class Solution(object):
    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: List[str]
        """
        wordDict = set(wordDict)
        res = []
        self.dfs(s, wordDict, 0, '', res)
        return res
    
    def check(self, s, wordDict):
        n = len(s)
        dp = [False] * (n + 1)    # s[:i]
        dp[0] = True
        for i in range(1, n+1):
            for j in range(i-1, -1, -1):
                if dp[j] and s[j:i] in wordDict:
                    dp[i] = True
                    break
        return dp[-1]
    
    def dfs(self, s, wordDict, i, temp, res):
        if i == len(s):
            res.append(temp[:-1])
        if self.check(s[i:], wordDict):
            for j in range(i+1, len(s)+1):
                if s[i:j] in wordDict:
                    self.dfs(s, wordDict, j, temp + s[i:j] + ' ', res) 

(改进-法1)
dp已经统计了截止当前位置的字符串是否可以由wordDict中的词汇组成。

class Solution(object):
    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: List[str]
        """
        wordDict = set(wordDict)
        dp = self.check(s, wordDict)
        res = []
        self.dfs(s, wordDict, dp, '', res)
        return res
    
    def check(self, s, wordDict):
        n = len(s)
        dp = [False] * (n+1)
        dp[0] = True
        for i in range(1, n+1):
            for j in range(i-1, -1, -1):
                if dp[j] and s[j:i] in wordDict:
                    dp[i] = True
                    break
        return dp
    
    def dfs(self, s, wordDict, dp, temp, res):
        if not s:
            res.append(temp[:-1])
        for i in range(len(s)):
            if dp[i] and s[i:] in wordDict:
                self.dfs(s[:i], wordDict, dp, s[i:] + ' ' + temp, res)

解法2
DP。dp[i]表示截止s[:i], 满足题意的所有可能组合

class Solution(object):
    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: List[str]
        """
        wordDict = set(wordDict)
        n = len(s)
        dp = [[] for _ in range(n+1)]    # 截止s[:i], 满足题意的所有可能组合
        dp[0] = ['']
        
        if self.check(s, wordDict):
            for i in range(1, n+1):
                temp = []
                for j in range(i):
                    if dp[j] and s[j:i] in wordDict:
                        for prev in dp[j]:
                            space = ' ' if prev else ''
                            temp.append(prev + space + s[j:i])
                dp[i] = temp
        return dp[-1]
    
    def check(self, s, wordDict):
        n = len(s)
        dp = [False] * (n+1)
        dp[0] = True
        for i in range(1, n+1):
            for j in range(i-1, -1, -1):
                if dp[j] and s[j:i] in wordDict:
                    dp[i] = True
                    break
        return dp[-1]

10. Regular Expression Matching

题目描述

给定一输入字符串s和一模式p,对两者进行正则表达式匹配(只考虑 ‘.’ 和 ‘*’)。
s可以为空或只包含a-z小写字母;p可以为空或只包含a-z小写字母或’.’ / ‘*’。

’.’ 匹配任意单字符’*’ 匹配0次或多次前缀元素

例子
Example 1:

Input:
s = “aa”
p = “a”
Output: false

Explanation: “a” does not match the entire string “aa”.

Example 2:

Input:
s = “aa”
p = “a*”
Output: true

Explanation: ‘*’ means zero or more of the precedeng element, ‘a’. Therefore, by repeating ‘a’ once, it becomes “aa”.

Example 3:

Input:
s = “ab”
p = “.*”
Output: true

Explanation: “.*” means “zero or more (*) of any character (.)”.

Example 4:

Input:
s = “aab”
p = “c*a*b”
Output: true

Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches “aab”.

Example 5:

Input:
s = “mississippi”
p = “mis*is*p*.”
Output: false

思想
(法1 - 递归)
首先判断第2位是不是*。(Yes)重复0次/第一位已匹配,重复多次[重复1次];(No)判断第一位匹配,s[1:]和p[1:]递归

(法2 - DP)
dp[i][j] 表示 s[:i] 和 p[:j] 是否匹配成功
初始化:dp[0][0] - 两个空字符串匹配成功;dp[0][j] - 空字符串只和*匹配成功。
状态方程
1)p[j-1] == ‘.’ or s[i-1],则dp[i][j] = dp[i-1][j-1] ---- p当前字符和s当前字符匹配;
2)p[j-1] == ‘*’ ---- p当前字符为 ‘*’
  2.1) p[j-2] == s[i-1] or ‘*’,则dp[i][j] = dp[i][j-2] or dp[i-1][j-2] or dp[i-1][j] ---- p*前面字符和s当前字符匹配,*匹配0次/1次/多次
  2.2) p[j-2] != s[i-1],则dp[i][j] = dp[i][j-2] ---- p*前面字符和s当前字符不匹配,*匹配0次;

解法1
递归,首先判断第2位是不是*。

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        if not s and not p:
            return True
        if not p and s:
            return False
        
        # 首先判断第2位是不是*
        if len(p) > 1 and p[1] == '*':
            if self.isMatch(s, p[2:]):    # 重复0次
                return True
            if s and (p[0] == '.' or p[0] == s[0]):    # 第一位已经匹配, 重复多次/1次
                return self.isMatch(s[1:], p)

                
        if s and (p[0] == '.' or p[0] == s[0]):
            return self.isMatch(s[1:], p[1:])
        return False

解法2
DP。根据当前为字符是否匹配;当前位字符是否为/* → 前一位字符是否匹配

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        m, n = len(s), len(p)
        dp = [[False] * (n+1) for _ in range(m+1)]  # dp[i][j] - s[:i] and p[:j]

        dp[0][0] = True
        for j in range(1, n+1):
            if p[j-1] == '*' and dp[0][j-2]:
                dp[0][j] = True
        
        for i in range(1, m+1):
            for j in range(1, n+1):
                if p[j-1] == s[i-1] or p[j-1] == '.':
                    dp[i][j] = dp[i-1][j-1]
                elif p[j-1] == '*' and j > 1:
                    if p[j-2] == s[i-1] or p[j-2] == '.':
                        dp[i][j] = dp[i][j-2] or dp[i-1][j-2] or dp[i-1][j]  # 0 times/1 times/multiple times
                    else:
                        dp[i][j] = dp[i][j-2]    # zero times                 
        return dp[-1][-1]

你可能感兴趣的:(Leetcode,DP,Word,Break,Regular,Expression,Matching)