Leetcode44 Wildcard Matching

本文深入探讨了WildcardMatching算法的实现,包括递归解决方案、改进的递归方法、动态规划和非递归回溯法。通过实例解析了如何使用星号(*)和问号(?)匹配字符串,提供了不同解法的时间复杂度分析,以及如何优化算法以提高效率。

Wildcard Matching

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

Solution1

  • 最简单的是递归,在《剑指offer》里也有类似的题目,便是用递归来解决的
public class Solution {
    public boolean isMatch(String s, String p) {
        return isMatch(s,p,0,0);
    }
    public boolean isMatch(String s,String p,int i,int j){
        if(i==s.length()&&j==p.length()) return true;
        if(i>s.length()||j==p.length()) return false;//i已超出2个字符以上,或者j到了末尾但i还未结束
        if(p.charAt(j)=='*') return isMatch(s,p,i,j+1)||isMatch(s,p,i+1,j+1)||isMatch(s,p,i+1,j);
        if(i==s.length()) return false;
        if(s.charAt(i)==p.charAt(j)||p.charAt(j)=='?') return isMatch(s,p,i+1,j+1);
        return false;        
    }
}

Solution2

  • 解法一的递归极其低效,虽然能解,但是太耗时间。这里换一种递归方法
public class Solution {
    public boolean isMatch(String s, String p) {
        return isMatch(s,p,0,0);
    }
    public boolean isMatch(String s,String p,int i,int j){
        if(i==s.length()&&j==p.length()) return true;
        if(j==p.length()) return false;
        if(p.charAt(j)=='*'){
            while(j<p.length()&&p.charAt(j)=='*') j++;//将'*'都跳过
            if(j==p.length()) return true;
            if(p.charAt(j)!='?') while(i<s.length()&&s.charAt(i)!=p.charAt(j)) i++;
            if(i==s.length()) return false;
            return isMatch(s,p,i+1,j+1)||isMatch(s,p,i+1,j);
        }
        if(i==s.length()) return false;
        if(s.charAt(i)==p.charAt(j)||p.charAt(j)=='?') return isMatch(s,p,i+1,j+1)||((j>0&&p.charAt(j-1)=='*')?isMatch(s,p,i+1,j):false);
        return (j>0&&p.charAt(j-1)=='*')?isMatch(s,p,i+1,j):false;
    }
}

Solution3

  • 解法二比解法一要好很多,主要是跳过了星号的判断,可以节省不少时间,但是递归仍然重复了很多次判断,这时候必须考虑到动态规划的解法了。
  • 一般说来,递归和动态规划有着明显的区别:递归往往都是从前至后或者从上往下(比如二叉树里面),而动态规划则往往都是从后往前或者从下往上(指思考过程,实际的运行过程仍然是从前往后从顶往下的,因为要记录中间过程)。动态规划的好处是在从后往前或从下往上的过程中,实际可以记录一些中间过程。这样便省去了很多重复过程。
public class Solution {
    public boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();
        boolean[][] dp = new boolean[m+1][n+1];//dp[i][j]为s中的前i段能匹配到p中j处
        dp[0][0] = true;
        for(int j=0;j<n;j++){
            dp[0][j+1] = dp[0][j]&&(p.charAt(j)=='*');
            for(int i=0;i<m;i++){
                if(p.charAt(j)=='*') dp[i+1][j+1] = dp[i+1][j]||dp[i][j]||dp[i][j+1];//此处的if-else语句便是本题动态规划的递推关系式
                else dp[i+1][j+1] = dp[i][j]&&(p.charAt(j)=='?'||p.charAt(j)==s.charAt(i));
            }
        }
        return dp[m][n];
    }
}

Solution4

  • 用滚动数组将解法三中的递推数组进一步简化为一维数组
public class Solution {
    public boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();
        boolean[] dp = new boolean[m+1];
        dp[0] = true;
        for(int j=0;j<n;j++){
            if(p.charAt(j)=='*') for(int i=0;i<m;i++) dp[i+1] = dp[i+1]||dp[i];
            else for(int i=m-1;i>=0;i--) dp[i+1] = dp[i]&&(p.charAt(j)=='?'||p.charAt(j)==s.charAt(i));
            dp[0] = dp[0]&&(p.charAt(j)=='*');//这句话必须放在末尾
        }
        return dp[m];
    }
}

Solution5

  • 还有一种更精妙绝伦的解法。本质上是回溯法,但不需要递归,并且比递归效率高出很多。
    非递归的回溯解法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值