(Java)LeetCode-44.Wildcard Matching

本文介绍了一种解决通配符匹配问题的有效算法,通过避免大量重复计算子问题,利用回溯技巧实现高效匹配,适用于模式中包含 '?' 和 '*' 的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Implement wildcard pattern matching with support for '?' and '*'.

'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false


这道题刚开始看的时候,以为是第十题的简化版本,于是复习了一下该题思路,写起来蛮简单的。

但是居然超时了。。谁家面试的时候出这么难题啊。。最近遇到好多难题(两个字符串距离,最大值的最小值,最短路径blabla..),血条空了

后来在网上看到一个思路,用的回溯算法。链接如下 http://www.cnblogs.com/felixfang/p/3708999.html

思路是(摘抄):

    超时原因在于即便使用了带记录的递归,对于p上的每一个'*',依然需要考虑'*' 匹配之后字符的所有情况,比如p = "c*ab*c",s = "cddabbac"时,遇到第一个'*',我们需要用递归处理p的剩余部分"ab*c" 和s的剩余部分"ddabbac"的所有尾部子集匹配。也就是:"ab*c"和"ddabbac","ab*c" 和"dabbac"的匹配,"ab*c" 和"abbac"的匹配,... ,"ab*c" 和"c"的匹配,"ab*c" 和"\0"的匹配。

    遇到第二个'*',依然如此。每一个'*'都意味着p的剩余部分要和s的剩余部分的所有尾子集匹配一遍。然而,我们如果仔细想想,实际上,当p中'*'的数量大于1个时,我们并不需要像上面一样匹配所有尾子集。

    依然以 p = "c*ab*c",s = "cddabbac"为例。

    对于p = "c*ab*c",我们可以猜想出它可以匹配的s应该长成这样: "c....ab.....c",省略号表示0到任意多的字符。我们发现主要就是p的中间那个"ab"比较麻烦,一定要s中的'ab'来匹配,因此只要s中间存在一个"ab",那么一切都可以交给后面的'*'了。

    所以说,当我们挨个比较p和s上的字符时,当我们遇到p的第一个'*',我们实际只需要不断地在s的剩余部分找和'ab'匹配的部分。

    换言之,我们可以记录下遇到*时p和s的位置,记为presp和press,然后挨个继续比较*(++p)和*(++s);如果发现*p != *s,就回溯回去,p = presp,s = press+1, ++press;直到比较到末尾,或者遇到了下一个'*',如果遇到了下一个'*',说明 "ab"部分搞定了,下面的就交给第二个'*'了;如果p和s都到末尾了,那么就返回true;如果到末尾了既没遇到新的'*',又还存在不匹配的值,press也已经到末尾了,那么就返回false了。

    这样的思路和上面的递归比起来,最大的区别就在于:

    遇到'*',我们只考虑遇到下一个'*'前的子问题,而不是考虑一直到末尾的子问题。从而避免大量的子问题计算。

    我们通过记录 presp和press,每次回溯的方法,避免使用递归。


不过他是用C++写的,读懂其思路后,改写为Java版本,即可AC,代码如下


public class Solution {
    public boolean isMatch(String s, String p) {
    	int is = 0;
    	int ip = 0;
    	
    	int press = 0;
    	int presp = 0;
    	
    	boolean backstrack = false;
    	for( is = 0; is < s.length(); ){
    		if( ip == p.length()){
    			if(backstrack == false){
    				return false;
    			}else if(p.charAt(p.length()-1) == '*'){
    				return true;
    			}
    			else {
    				ip = presp;
    				is = ++press;
    			}
    		}
    		if(p.charAt(ip) == '?'){
    			is++;
    			ip++;
    		}else if(p.charAt(ip) == '*'){
    			presp = ++ip;
    			press = is;
    			backstrack = true;
    		}else{
    			if(p.charAt(ip) == s.charAt(is)){
    				is++;
        			ip++;
    			}else if(backstrack){
    				ip = presp;
    				is = ++press;
    			}else{
    				return false;
    			}
    		}
    	}
    	while(ip <= p.length() - 1 && p.charAt(ip) == '*' ){
    		ip ++;
    		if( ip == p.length()){
    			break;
    		}
    	}
    	return ip == p.length();
    }
    
    public static void main(String[] args){
    	Solution sol = new Solution();
    	System.out.println(sol.isMatch("aa", "a"));
    	System.out.println(sol.isMatch("aa", "aa"));
    	System.out.println(sol.isMatch("aaa", "aa"));
    	System.out.println(sol.isMatch("aa", "*"));
    	System.out.println(sol.isMatch("aa", "a*"));
    	System.out.println(sol.isMatch("ab", "?*"));
    	System.out.println(sol.isMatch("aab", "c*a*b"));
    	System.out.println(sol.isMatch("ab", "*a"));
    	System.out.println(sol.isMatch("hi", "*?"));
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值