LeetCode算法(8)---通配符匹配

题目

给定一个字符串 (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

我的答案

思路
(1)如果p或者s为空,很好判断1
(2)如果p的第一位不为 ’ * ',则当且仅当首字母匹配且F(s-1,p-1)匹配 才true
(3)如果p的第一位为 ’ * ',则分以下几种情况

1、p中只剩一个 ’ * ’ 了,则肯定true
2、找到p中的下一个非’ * ’ 符号,如果是 ’ ? ’ 则继续找,然后在s中找到它的位置。若F(s-k,p-next)为true,说明s的后面匹配p的后面部分,则返回true

代码如下

    public static boolean isMatch(String s, String p) {
        //如果p为空,当且仅当s为空
    	if(p.isEmpty())
        	return s.isEmpty();
        
    	//如果s为空,当且仅当p全为***
    	if(s.isEmpty()) {
    		int index = 0;
    		while(p.charAt(index++) == '*') {
    			//如果找完了都还是*,那么p全部为*****,一定true
        		if(index==p.length()) return true;
    		}
    		return false;
    	}
    	
    	if(p.replace("*", "").length()>s.length()) 
    		return false;
    	
        //若p第一个不为*,则当且仅当首字母匹配,且F(s-1,p-1)匹配
        if(p.charAt(0)!='*') {
        	return (p.charAt(0)=='?'||s.charAt(0)==p.charAt(0))&&isMatch(s.substring(1),p.substring(1));
        }
        
        //若p第一位为*,则分为以下几种情况
        if(p.charAt(0)=='*') {
        	//若p只含有这个*了,则true
        	if(p.length()==1)
        		return true;
        	
        	//找到P的下一个非*字符,序号为next
        	int next = 0;
    		int numX = 0;//记录?的个数
        	while(p.charAt(next)=='*'||p.charAt(next)=='?') {
 
        		if(p.charAt(next)=='?') {//如果是问号的话
        			//System.out.println("找到一个问号,位置"+next);
        			numX++;
        			next++;
        			if(numX>s.length()) return false;//?个数比s的长度还大
            		if(next==p.length()) return true;//长度检测
            		continue;
        		}
        		next++;
        		if(next==p.length()) return true;//长度检测
        		
        	}
        	
        	//从s中找这个字符,其序号为k
        	int k = s.indexOf(p.charAt(next));
        	

        	//System.out.println("s:"+s+", p:"+p+", next:"+next+", k:"+k+" ,numX:"+numX);
        	
        	//如果s中找不到,则不匹配
        	if(k == -1) return false;
        	
        	//若问号的个数比s中找到的第一个匹配字符还大的话,该匹配字符不可用!
        	if(numX>k) {
        		System.out.println("numX>k,匹配s="+s.substring(numX)+", p="+('*'+p.substring(next)));
        		return isMatch(s.substring(numX), '*'+p.substring(next));
        	}
        	
        	//若s-k和p-next匹配了,就true
        	if(isMatch(s.substring(k), p.substring(next)))
        		return true;
        	else if(k ==0) {
        		return isMatch(s.substring(1), p);
        	}else
        		return isMatch(s.substring(k), p);
        }
    	
    	return true;
    }

动态规划

思路解析
使用 boolean[][] m 来表示s(0 ~ i-1)和 p(0 ~ j-1)是否匹配
m[i][j]而言
p(j-1) == ' * ' ,则当且仅当 当前字符匹配s(i-1)==p(j-1)m[s-1,p-1]true时才匹配
p(j-1) != ' * ' , 则当m[i][j-1]true 或者 m[i-1][j]true的时候 匹配

其中:
m[i][j-1] 即当前’‘匹配一个空字符
m[i-1][j] 即当前’
'匹配一个字符s.charAt(i-1)

**注意!!!**动态规划中用的自底向上的算法,与普通迭代算法自顶向下的迭代思路会有不同,一般动态规划更加简洁清晰!

代码如下

//别人的动态规划解法
    //m[i][j]表示s(0 ~ i-1)和 p(0 ~ j-1)是否匹配
    public static boolean isMatch3(String s, String p) {
        boolean[][] m = new boolean[s.length()+1][p.length()+1];
        
        m[0][0] = true; //两个字符串都为空是肯定匹配
        for(int i = 0; i <= s.length(); i++) {
            for(int j = 1; j <= p.length(); j++) {
                if(p.charAt(j-1) == '*') {
                    //m[i][j-1] 即当前'*'匹配一个空字符
                    //m[i-1][j] 即当前'*'匹配一个字符s.charAt(i-1)
                    //这里之所以不是m[i-1][j-1]是因为当前'*'可能在前面一匹配了若干个字符
                    m[i][j] = m[i][j-1] || (i > 0 && m[i-1][j]);
                } else {
                    //前面的m(0~i-2)和p(0~j-2)能匹配且当前字符能匹配
                    m[i][j] = i > 0 && m[i-1][j-1] && (s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '?');
                }
            }
        }
        
        return m[s.length()][p.length()];
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值