leetcode.44 通配符匹配解决过程

leetcode 44.通配符匹配

原题链接

题目

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

‘?’ 可以匹配任何单个字符。
‘*’ 可以匹配任意字符串(包括空字符串)。

两个字符串完全匹配才算匹配成功。

说明:

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

解题过程

1.最开始的解题思路。

因为这题的通配符匹配和leetcode.10 正则表达式匹配 很像,所以最开始想用类似的思路解决。

bool isMatchCore(const string& s,const string& p,int i1,int i2){
    //i1到达尾判断i2是否到达尾或者,返回true否则false
    //为什么不是i1到达尾:因为i1到尾i2没到也可以i2及其以后全部为*表示空
    if(i1==len1){
        while(i2<len2 && p[i2]=='*') i2++;
        return i2==len2;
    }

    //当前pattern字符串的当前字符为'*'
    if(i2<len2&& p[i2]=='*'){
        return
            //i2不动,i1前进1格(匹配多个字符情况 
            isMatchCore(s,p,i1+1,i2) 
            //i1不动,i2前进一格说明匹配空字符
            || isMatchCore(s,p,i1,i2+1);
    }        
    
    //当前p不为*,且
    //在i1非尾的情况下,当前字符一样或者p字符为?,各前进一格
    if(i2<len2 && (s[i1]==p[i2]||p[i2]=='?')){
        return isMatchCore(s,p,i1+1,i2+1);
    }

    return false;
}

在源函数 isMatch 中我们只需要调用isMatchCore函数且索引i1、i2设置为0即可。

bool isMatch(string s, string p)

结果是超时:超时情况1

2.改进思路1中的解法

事实上我们可以发现,由于’ * '号的增加,递归栈也在不停地调用,深度过大耗时也会过大。不难发现 ’ * ‘号连续时,无论有多少个都相当于1个,所以在源函数中可进行处理,将连续的’ * '号进行删除只留一个。在源函数isMatch中可以进行改进:

bool isMatch(string s, string p) {
    len1=s.size();
    len2=p.size();
    //两空为true
    if(len1==0&&len2==0) return true;

    //2个以上连着的*号其实都只算一个*号
    int i2=0;
    while(i2<p.size()){
        //若当前非*,找到第一个*
        int cur_len=p.size();
        while(i2<cur_len && p[i2]!='*'){
            i2++;
        }

        if(i2<cur_len &&p[i2]=='*' ){
            int start=i2;
            while(i2<cur_len&& p[i2]=='*'){
                i2++;
            }
            //清除多余*
            p.erase(start,i2-start-1);
            //重置位置
            i2=start+1;
        }


    }
    len2= p.size();

    return isMatchCore(s,p,0,0);
}

结果是:仍然超时。
超时情况2

3.解决方法

经过之前两次的超时说明这个模仿leetcode.10
正则表达式匹配的回溯法难以通过。 用动态规划试试。
创建一个二维向量dp[m+1][n+1],其中m、n为s、p的长度,dp[i][j]表示s、p的前i、j个字符是否能匹配。

状态转移方程有:
1、当p[ j-1 ] == ‘?'或s[ i-1 ] == p[ j-1 ]时,dp[ i ] [ j ] = dp[ i-1 ] [ j-1 ]即完全由前i-1个s和前j-1个p决定当前是否能匹配。
2、当p[ j-1 ]==’ * ‘时,dp[ i ] [ j ]= dp[ i-1 ] [ j ] || dp[ i ] [ j-1 ]。即当前能否匹配取决于当前位置(‘ * ’为空)dp[ i ] [ j-1 ]的状态;或者dp[ i-1 ] [ j ]即不计当前s字符情况下,前j个p的字符串能否匹配,‘* ‘ 的作用即在前一个i-1、j状态下多充当一个字符s[i](‘ * ’非空)。

bool isMatch(string s, string p) {
    int m=s.size();
    int n=p.size();
    //dp数组,dp[i][j]表示s的前i个和p的前j个是否匹配,默认为false
    vector<vector<bool>> dp(m+1,vector<bool>(n+1,false));

    //初始化:(第一列全为false,表明当p为空不管s多少个都为空(除了均空的dp[0][0]
    dp[0][0]=true;
    for(int i=1;i<=n;++i){
        //当s为空,p只要前面匹配成功且当前为*,则也为成功匹配
        dp[0][i]=dp[0][i-1] && p[i-1]=='*';
    }

    
    for(int i_s=1;i_s<=m;i_s++){
        for(int i_p=1;i_p<=n;i_p++){
            //如果当前俩字符相同或p字符为'?'
            if(s[i_s-1]==p[i_p-1] || p[i_p-1]=='?'){
                dp[i_s][i_p]=dp[i_s-1][i_p-1];
            }
            //当前p字符为'*'
            else if(p[i_p-1]=='*'){
                dp[i_s][i_p]= dp[i_s-1][i_p] || dp[i_s][i_p-1];
            }

        }
    }

    return dp[m][n];
}

4.感想

本题的’ * ‘和之前的正则表达式星号不是一个量级,能匹配0-无穷多个任意字符,相比正则表达式的’ * '只能复制其前一个的若干个,少了很多限制,让递归深度过大,导致超时。
但是动态规划是不会出现这种情况的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值