leetcode ---- 动态规划(困难) :正则表达式匹配(python)

1.  问题:

给定一个字符串s和一个字符模式p,实现支持 "." 和 "*"的正则表达式匹配。

"."  匹配任意单个字符, "*"  匹配0个或者多个前面的元素。匹配应该覆盖整个字符串s,而不是部分字符串。

说明:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *

示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: '*' 代表可匹配零个或多个前面的元素, 即可以匹配 'a' 。因此, 重复 'a' 一次, 字符串可变为 "aa"。

示例 3:

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 'c' 可以不被重复, 'a' 可以被重复一次。因此可以匹配字符串 "aab"。

示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false

2.  问题分析:

动态规划的思路:找到一个递推公式,由前向后或者由后向前求解题目。

在字符串匹配问题中,基本思想是:从前往后,维护两个指针(一个指针遍历s,一个指针遍历p,并不断判断当前两指针的子串是否匹配),这种思想可以用一个二维布尔表dp来实现。

 二维表格dp的大小为(len(s)+1)*(len(p)+1)dp[i][j]表示,若s的子串[0,i)和p的子串[0,j)匹配,dp[i][j]=True ,若不匹配dp[i][j]=False。最后返回的就是右下角位置的数字。

2.1 首先完善dp表的第一行和第一列。

      当 p 为空串时,只有目标串 s 的空串才能与 p 匹配;

      当 s 为空串时,只有 p 为 空串 或者 为 x*y* 的形式才能与 s 匹配。

#2.1 初始化dp表,初始化表的第一列和第一行
dp = [[False for j in range(len(p)+1)] for i in range(len(s)+1)]    #  初始化dp表
dp[0][0] = True                                                     #  s和p 都为空时匹配
#  s 为空串时
for j in range(1, len(p)+1):                                        #  [j-1]为p的真实索引
    dp[0][j] = (j>=2) and (p[j-1]=="*") and dp[0][j-2]              #   只有x*能匹配空串,若有*,它的真值一定和dp[0][j-2]的相同

#  注意:and的语句可以转换成if判断语句,如: dp[0][j] = (j>=2) and (p[j-1]=="*") and dp[0][j-2],可转化为如下if语句:
     #if p[i-1]=='*':
         #if i>=2:
             #dp[0][i]=dp[0][i-2]

2.2 然后两层循环填充剩下的部分。

      每次s字符串往下走一个字符,和所有的p子串进行匹配,接下来分两种情况进行分类。

      ( a )  假设当前位置为dp[i][j],若p[j-1] == "*"时,"*"的用法分为两种(1:代表空串  2:代表一个或者多个前一个字符)

要想dp[i][j] =1,需要满足下列条件中的任一个:

                    (a.1)dp[i][j-2] =1时,此时"*"代表空串

                    (a.2)dp[i-1][j] =1时满足(p[j-2]==s[i-1] or p[j-2]=="."),此时"*"代表对前一字符的复制

      ( b )  若p[j-1]!= "*"时,要想dp[i][j] =1,需满足(p[j-1]==s[i-1] or p[j-1]=="."),还要判断前面的是否匹配,即dp[i-1][j-1]的值是否为True。

3.  图解例子:

假设:  s="aaaa"   p="a*b*"  求两者是否匹配,做下图所示:

红色的箭头: p[j-1] != "*"时,

绿色的箭头:p[j-1] == "*"时, "*" 代表空串

黄色的箭头:p[j-1] == "*"时, "*" 代表一个或者多个前面的元素

4.  程序:

class Solution:
    def isMatch(self, s, p):
        dp=[[False for j in range(len(p)+1)] for i in range(len(s)+1)] #  初始化二维表dp
        print(dp)
        dp[0][0]=True                                                   #  s 和 p 都为空时
        #  若 s 为空时
        for j in range(1,len(p)+1):
            #dp[0][j]= (p[j-1]=="*")and(j>=2)and(dp[0][j-2])            #  等同于下列语句
            if p[j-1]=='*':
                if j>=2:
                    dp[0][j]=dp[0][j-2]
        print(dp)

        for i in range(1,len(s)+1):
            for j in range(1,len(p)+1):
                #  j-1才为正常字符串中的索引
                #  p当前位置为"*"时,(代表空串--dp[i][j-2]、一个或者多个前一个字符--( dp[i-1][j] and (p[j-2]==s[i-1] or p[j-2]=='.'))
                if p[j-1]=='*':
                    dp[i][j]= dp[i][j-2] or ( dp[i-1][j] and (p[j-2]==s[i-1] or p[j-2]=='.') ) # dp[i][j-1] or
                #  p当前位置为"."时或者与s相同时,传递dp[i-1][j-1]的真值
                else:
                    dp[i][j]=(p[j-1]=='.' or p[j-1]==s[i-1]) and dp[i-1][j-1]

        return dp[len(s)][len(p)]

ss = Solution()
s="a"
p=".*"
print(ss.isMatch(s,p))

参考:

1.https://cloud.tencent.com/developer/article/1092371

2.https://hk029.gitbooks.io/leetbook/动态规划/010.%20Regular%20Expression%20Matching/010.%20Regular%20Expression%20Matching.html

猜你喜欢

转载自blog.csdn.net/weixin_39781462/article/details/82999610