ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

leetcode 第44题 通配符匹配

2021-04-18 14:29:12  阅读:171  来源: 互联网

标签:遍历 匹配 44 通配符 si pj indexS indexP leetcode


题目

给定一个字符串 (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 = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".
示例 5:

输入:
s = "acdcb"
p = "a*c?b"
输出: false

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/wildcard-matching

思路

        首先,题目里的复杂点有两个:问号和星号。但是问号其实是模糊匹配确定的一个字符,这个反而是一个非常简单的元素,所以星号才是真正使这个题变复杂的点。

        这个问题一看就知道是可以进行分解成子问题的。我们按照逻辑上的自然顺序对s和p进行匹配,都是先从第一个字符开始匹配,考虑各种情况之后往后进行匹配,直到s和p都遍历完,确定可以匹配成功或者不成功为止。对于普通的a-z字符,直接对比即可;对于问号,直接当做匹配即可;对于星号,有三种情况,我们另indexS为s的遍历指针,indexP为p的遍历指针:(1)星号包含s的当前字符且包含之后的字符,则indexS+1, indexP不变(2)星号不包含s的当前字符,则indexS不变,indexP+1(3)星号正好与s的当前字符匹配,则indexS+1,indexP+1。其实第三种情况可以被钱两种情况的组合覆盖到,所以只需要保留前两种情况即可。

       这时候我们就可以开始写代码实现了。但是上面的流程中很显然有很多重复的计算,所以我们需要做一下剪枝。比如已经判断过indexS到s.length的字符串与indexP到p.length的pattern是匹配的,就可以记录下来,下次不需要重新计算。

        上面的代码实现显然是用递归实现,那么就需要考虑递归的结束条件:(1)如果s和p都已经遍历完成,那么就说明已经匹配成功(2)如果p遍历完成,s没有遍历完成,那么说明匹配失败(3)如果s遍历完成,p的剩余字符都是星号,那么匹配也是成功,否则匹配失败

        到此,整个代码的流程就已经确定了。

        额外的,我们可以写一下状态转移方程。上面过程中我们提到动态规划,但是最终使用了剪枝方法,是用到了动态规划的思想的。那么总结成动态规划的状态转移方程是完全有必要的。其实我认为动态规划的问题最难就在于把问题归纳为一个数学题,比如我们上面的思路其实是在求解这样一个问题:对于s和p,求解s[0,end]和p[0,end]的匹配情况,因为end是确定的,所以可以忽略,也就是求解f(s0,p0)。我们泛化为求解f(si, pj)。在s[i]=p[j]或者p[j]=='?'的情况下,f(si,pj)=f(si+1,pj+1);在p[j]='*'的情况下,f(si,pj)=f(si+1,pj) or f(si,pj+1);其他情况下f(si,pj)=false。

        我们看官方的解法,其实正好相反,我把end当作是确定的,他把0当作是确认的,所以题目被归纳为求解f(sn,pm)。然后泛化为求解f(si,pj)。在s[i]=p[j]或者p[j]=='?'的情况下,f(si,pj)=f(si-1,pj-1);在p[j]='*'的情况下,f(si,pj)=f(si-1,pj) or f(si,pj-1);其他情况下f(si,pj)=false。

        这两个思路看起来一样,但我觉得官方的思路更好一些,因为这个思路是一个显式地逐步增加信息的过程,用f(si,pj)去往前推进。总结当前信息,然后向下推进,这种可以用迭代的方式实现,而不必是递归。

个人代码如下:

class Solution {
    public boolean isMatch(String s, String p) {
        // 用于记录s[i,end]和p[j,end]的匹配情况;0代表未知,1代表匹配,-1代表不匹配
        int[][] matchCase = new int[s.length()][p.length()];
        return isMatch(s, 0, p, 0, matchCase);
    }

    public boolean isMatch(String s, int indexS, String p, int indexP, int [][]matchCase) {
        // s已遍历完,p也遍历完则match;p剩余全是星号则匹配,否则不匹配
        if (indexS == s.length()) {
            if (indexP == p.length()) {
                return true;
            } else {
                while (indexP < p.length()) {
                    if (p.charAt(indexP) != '*') {
                        return false;
                    }
                    indexP++;
                }
                return true;
            }
        }
        // p遍历完,s没遍历完,则不匹配
        if (indexP == p.length()) {
            return false;
        }
        // 剪枝,使用已经计算过的结果
        if (matchCase[indexS][indexP] != 0) {
            return matchCase[indexS][indexP] == 1;
        }

        char charS = s.charAt(indexS);
        char charP = p.charAt(indexP);
        boolean match = false;
        if (charP == charS || charP == '?') {
            match = isMatch(s, indexS + 1, p, indexP + 1,matchCase);
        } else if (charP == '*') {
            match = isMatch(s, indexS + 1, p, indexP + 1,matchCase) || isMatch(s, indexS, p, indexP + 1,matchCase) || isMatch(s, indexS + 1, p, indexP,matchCase);
        }
        matchCase[indexS][indexP] = match ? 1 : -1;
        return match;
    }

    public static void main(String[] args) {
        String s = "adceb";
        String p = "*a*b";
        System.out.println(new Solution().isMatch(s,p));
    }
}

复杂度

时间复杂度:O(mn)

空间复杂度:O(mn)

耗时:78分钟

 

 

 

标签:遍历,匹配,44,通配符,si,pj,indexS,indexP,leetcode
来源: https://blog.csdn.net/lijianqingfeng/article/details/115827580

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有