题目描述

给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。

‘?’ 可以匹配任何单个字符。
‘*’ 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
示例 1:

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


示例 2:

输入:
 s = “aa”
 p = ""
 输出: true
 解释: '’ 可以匹配任意字符串。


示例 3:

输入:
 s = “cb”
 p = “?a”
 输出: false
 解释: ‘?’ 可以匹配 ‘c’, 但第二个 ‘a’ 无法匹配 ‘b’。


示例 4:

输入:
 s = “adceb”
 p = “ab”
 输出: true
 解释: 第一个 ‘’ 可以匹配空字符串, 第二个 '’ 可以匹配字符串 “dce”.


示例 5:

输入:
 s = “acdcb”
 p = “a*c?b”
 输入: false

解题思路

需要明确的是 用 “模式串” 去匹配 “字符串” ,循环是 模式串在外层循环

for p:
       for s:

暴力破解1

采用递归的思路 碰到 == *== 特殊考虑

solve(i_s,i_p+1)  \  * 匹配0 个
		 or   solve(i_s+1,i_p+1) \  *匹配结束
		 or solve(i_s+1,i_p))	 * 继续匹配下一个

代码

def isMatch_brude_force(s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        len_s=len(s)
        len_p=len(p)
        def solve(i_s,i_p):
            if i_s > len_s or i_p > len_p:
                return False
            if i_s == len_s:
                if i_p==len_p:
                    return True
                elif len(set(p[i_p:]))==1 and set(p[i_p:]).pop()=="*":
                    return True
                else:
                    return False
            elif i_p==len_p:
                return False
            if s[i_s]==p[i_p] or p[i_p]=="?":
                i_s+=1
                i_p+=1
                return solve(i_s,i_p)
            elif p[i_p]=="*":
                return solve(i_s,i_p+1) or solve(i_s+1,i_p+1) or solve(i_s+1,i_p)
            else:
                return False

        return solve(0,0)

动态规划解答2

既然用到动态规划,最重要的是设置初值 和找到递推式

我们开始分析初值怎么设;其实很简单,把这个匹配问题可以想象成一个矩阵dp,纵轴代表含有通配符的匹配字符串s2, 横轴代表要匹配的字符串s1。假设现在s2=”a*b”, s1=”abc” 如图:

python带通配符的字符串查找替换 python 通配符 字符串_初值

对应空位就是截止到当前的 (i,j) 位置,两字符串是否匹配。匹配为 T(true),不匹配为 F(false),最后返回最右下角的值,就是当前两个字符串是否匹配的最终值;

现在我们要做的设置初值,所以我们大可多加一行和一列,来填充初值;s1既然是要匹配的,我们都设为 F(即dp[0][1]=F,dp[0][2]=F,dp[0][3]=F),表示当前还未开始匹配。而s2的初值,我们发现如果星号和a调换位置,星号可以匹配任意字符串,所以dp[i][0]的值取决于该位置是否为星号和上一个位置d[i-1][0]是否为T(其实就是上一个位置是否也是星号),所以我们设置dp[0][0]为 T。所以形成下图:

python带通配符的字符串查找替换 python 通配符 字符串_python带通配符的字符串查找替换_02


此时初值已经设置完毕,我们要找到递推式;经局部推算,我们发现递推式应该有两种,一种是当s2的字符是星号,另一种是s2的字符是非星号。先看星号的情况:当要计算dp[2][1](即要匹配a和a时),我们发现是取决于dp[1][1](即a和a是否匹配),当要计算dp[2][2] (即要匹配a和ab时),是取决于dp[2][1] (即a*和a是否匹配)。抽象一下,星号和任意字符(0或多个)都匹配。所以字符串截止到星号匹配的情况,取决于当前位置向上和向左的情况(即可以为0个字符,也可以为多个字符)。

所以此时递推式为dp[i][j]=dp[i−1][j]||dp[i][j−1]dp[i][j]=dp[i−1][j]||dp[i][j−1] 如图:

python带通配符的字符串查找替换 python 通配符 字符串_初值_03

再看非星号的情况:当要计算dp[3][2] (即要匹配ab和ab时),则取决于dp[2][1]和a[3][2] (即a和a是否匹配,同时b和b是否匹配);所以可以得到递推式 dp[i][j] = dp[i-1][j-1]&&a[i][j]dp[i][j] = dp[i-1][j-1]&&a[i][j]。如图:

python带通配符的字符串查找替换 python 通配符 字符串_初值_04

python3 代码实现

def isMatch_dp(s,p):
        len_s=len(s)
        len_p=len(p)
        dp=[[False]*(len_s+1) for _ in range(len_p+1)]
        dp[0][0]=True

        for i in range(1,len_p+1):
            char_p = p[i-1]
            dp[i][0] = (dp[i-1][0] and char_p == "*")
            for j in range(1,len_s+1):
                char_s=s[j-1]
                if char_p=="*":
                    dp[i][j]=(dp[i-1][j] or dp[i][j-1])
                else:
                    dp[i][j]=(dp[i-1][j-1] and (char_p=="?" or char_p == char_s))

        return dp[len_p][len_s]

下面这一种方法 trick (我没看,不过代码是正确的,我从其他地方复制过来的)

主要是星号的匹配处理。遇到星号就跳过去,优先用星号后面的来匹配。如果星号后面的匹配不上,则用星号匹配一个字符,再看星号后面的能否匹配上

def isMatch_most_difficult(s, p):
        scur, pcur, sstar, pstar = 0, 0, None, None
        while scur < len(s):
            if pcur < len(p) and p[pcur] in [s[scur], '?']:
                scur, pcur = scur+1, pcur+1
            elif pcur < len(p) and p[pcur] == '*':
                pstar, pcur = pcur, pcur+1
                sstar = scur
            elif pstar != None:
                pcur = pstar + 1
                sstar += 1
                scur = sstar
            else:
                return False

        while pcur < len(p) and p[pcur] == '*':
            pcur += 1

        return pcur >= len(p)