LeetCode10、正则表达式匹配(注意防止下标越位的问题)

题目描述

https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/
在这里插入图片描述

解法(递归思维、动态规划思维)

第一次,直接思考动态规划:

做不出来,下标问题:

class Solution {
    
    
    public boolean isMatch(String s, String p) {
    
    
    //边界先处理掉
      if(p==null || p.length()==0) return s==null?true:s.length()==0;

        //dp[i][j]表示s前i个元素匹配p前j个结果
        //这里要加1,则表示s 的前 i 个字符与 p 中的前 j 个字符是否能够匹配
        boolean [][]dp = new boolean[s.length()+1][p.length()+1];
//dp的边界处理
        dp[0][0] = true;//"" 配 ""为true
        //dp[0][..]都是false,dp[..][0]也是false

        
        //递推方程:
        for(int i=1;i<=s.length();i++){
    
    
            for(int j=1;j<=p.length();j++){
    
    
                if(s.charAt(i-1)==p.charAt(j-1) || p.charAt(j-1)=='.'){
    
    
                    dp[i][j] = dp[i-1][j-1];//匹配一个
                }
                else if(p.charAt(j-1)=='*' && j>=2){
    
    //必须两个以上
                		dp[i][j] = dp[i][j-2];//之前匹配的结果
                         if(s.charAt(i-1)==p.charAt(j-2)){
    
    //匹配1次则继续匹配
                                dp[i][j] = dp[i-1][j-1] || dp[i][j];
                        }else{
    
    //不匹配,则丢弃
                                    dp[i][j] = dp[i][j-2];//i不变,j直接跳两位
                         
                        }
                    }

                  
                }
            }

        return dp[s.length()][p.length()];
    }
}

第二次,一步一步思考,使用递归:
第一次匹配,看谁能走。分别处理三种情况:正常字符、.字符和*字符

class Solution {
    
    
    public boolean isMatch(String s, String p) {
    
    
        if(p==null || p.length()==0) return s==null?true:s.length()==0;
        boolean flag=false;//当前匹配的结果
        //匹配第一个字符
        if(s!=null &&s.length()>0 && p.length()>0){
    
    
            if(s.charAt(0)==p.charAt(0) || p.charAt(0)=='.'){
    
    //保留之前的匹配结果
                flag = true;
            }
        }
        if(s.length()==0 && p.length()==0) return true;
        //出来
       // 处理*号的问题
       if(p.length()>=2 && p.charAt(1)=='*'){
    
    //整体作为匹配,p【0】和p【1】
            //匹配0次,则直接跳过该* 或者选择匹配一次,那么s的下标移动
            return isMatch(s,p.substring(2)) || (flag && isMatch(s.substring(1),p));//匹配1次
       }else{
    
    //不匹配,直接跳
            return flag && isMatch(s.substring(1),p.substring(1));//普通的匹配
       }
        

    }
}

在这里插入图片描述
使用带备忘录的递归:
boolean的备忘录有点难搞,我们需要使用多一个数组来记录。

class Solution {
    
    
    boolean [][]memo;//记录结果
    boolean [][]res;//记录是否计算过
    public boolean isMatch(String s, String p) {
    
    
        memo = new boolean[s.length()+1][p.length()+1];
        res = new boolean[s.length()+1][p.length()+1];//创建大一点防止越界
        return isMatch(s,p,0,0);
    }
        public boolean isMatch(String s, String p,int i,int j) {
    
    
        if(res[i][j])
            return memo[i][j];
        if(p==null || p.length()==0){
    
    
            memo[i][j] = s==null?true:s.length()==0;
            res[i][j] = true;
            return memo[i][j];
        } 
        boolean flag=false;//当前匹配的结果
        //匹配第一个字符 
        if(s!=null && s.length()>0 && p.length()>0){
    
    
            if(s.charAt(0)==p.charAt(0) || p.charAt(0)=='.'){
    
    //保留之前的匹配结果
                flag = true;
            }
        }
        if(s.length()==0 && p.length()==0) {
    
    
            memo[i][j] = true;
            res[i][j] = true;;//计算过了
            return memo[i][j];
        };
        //出来
       // 处理*号的问题
       if(p.length()>=2 && p.charAt(1)=='*'){
    
    //整体作为匹配,p【0】和p【1】
            //匹配0次,则直接跳过该* 或者选择匹配一次,那么s的下标移动
            memo[i][j] =  isMatch(s,p.substring(2),i,j+2) || (flag && isMatch(s.substring(1),p,i+1,j));//匹配1次
            
       }else{
    
    //不匹配,直接跳
            memo[i][j] =  flag && isMatch(s.substring(1),p.substring(1),i+1,j+1);//普通的匹配
       }
        res[i][j] = true;
        return memo[i][j];
    }
}

在这里插入图片描述
动态规划

认真看题解,然后自己写了一遍:

最容易想错的,是没把类似“a*”整体匹配。
正确的:
dp[i][j] = dp[i][j] | dp[i-1][j];//非短路与
错误:
dp[i][j] = dp[i][j] | dp[i-1][j-1];

class Solution {
    
    
    public boolean isMatch(String s, String p) {
    
    
    //边界先处理掉
      if(p==null || p.length()==0) return s==null?true:s.length()==0;

        //dp[i][j]表示s前i个元素匹配p前j个结果
        //这里要加1,则表示s 的前 i 个字符与 p 中的前 j 个字符是否能够匹配
        boolean [][]dp = new boolean[s.length()+1][p.length()+1];
//dp的边界处理
        dp[0][0] = true;//"" 配 ""为true
        //""匹配  a*b* 是true dp[0][....]未知    所以下标i要从0开始
        //递推方程:
        for(int i=0;i<=s.length();i++){
    
    
            for(int j=1;j<=p.length();j++){
    
    
                if(p.charAt(j-1)=='*'){
    
    //需要匹配0次或者1次
                   // if(j>=2){
    
    
                        dp[i][j] = dp[i][j-2];
                   // }
                    //做选择:
                    if(isMatch(s,p,i,j-1)){
    
    
                        dp[i][j] = dp[i][j] //一次都没匹配
                                        |dp[i-1][j];// 匹配一次 -----------这里为什么不是j-1,我们这里必须整体左匹配,因为如果我们不是整体做匹配,这里如果选择了dp[i-1][j-1]下一轮则没有*了。      
                                        // or 两个都要计算,非短路或
                                        //匹配一次,这里就是匹配了一次之后的结果也不能决定总体的结果,
                                        //比如此时是false,但是我一次都不匹配却得到了true
                    }
                }
               else{
    
    
                   if(isMatch(s,p,i,j)){
    
    
                        dp[i][j] = dp[i-1][j-1];
                    }else{
    
    
                        dp[i][j] = false;
                    }
               }
            }
        }
        return dp[s.length()][p.length()];
    }
    /*
    *建立一个用于匹配的方法,匹配s的第i个字符 == p的第j个字符
    */
    public boolean isMatch(String s,String p,int i,int j){
    
    
        if(i==0){
    
    //都没有字符,
            return false;
        }
        if(p.charAt(j-1)=='.') return true;//  .匹配任意字符
        return s.charAt(i-1)==p.charAt(j-1);
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44861675/article/details/114708399