剑指--正则表达式匹配

剑指–正则表达式匹配

1,题目:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2,思路:

方法一:(回溯法)暴力方法:

在这里插入图片描述
在这里插入图片描述
所以说这算是一种暴力方法,会将所有的情况走一边,看看是否存在可以匹配的情况。

方法二:动态规划:

本题的dp数组的含义就是:dp[i][j]就是s的前i个元素是否可以被p的前j个元素所匹配。

我们知道了dp数组的含义之后就知道了dp数组的几个细节:

1.dp[0][0]一定是true,因为s为空且p也为空的时候一定是匹配的;2.dp[1][0]一定是false,因为s有一个字符但是p为空的时候一定是不匹配的。
3.这个boolean类型的dp数组的大小应该是dp[s.length+1][p.length+1],因为我们不仅仅要分别取出s和p的所有元素,还要表示分别取s和p的0个元素时候(都为空)的情况。
4.当写到dp[s.length][p.length]的时候,我们就得到了最终s和p的匹配情况。
5.dp[1][0]~dp[s.length][0]这一列都是false,因为s不为空但是p为空一定不能匹配。
6.所以创建好dp数组之后,初始化dp[0][0]=true、dp[0][1]=false、dp[1][0]~dp[s.length][0]都是false。然后将第一行即dp[0][2]到dp[0][p.length]的元素初始化。

7.第一行初始化思路:如果不为空的p想要匹配上为空的s,因为此时p已经不为空,则需要p是"a*"、“b*”、“c*”。。。这种形式的才能匹配上。

8.然后填写数组的其余部分,这个过程中如果p.charAt(j)==’*'依然是遵循上题中的两种情况;否则就判断两个字符串的i和j号字符是否相等,相等则分别减除当前字符继续判断,不相等则直接等于false。

动态规划写法二:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3,代码:

方法一:(回溯法)暴力方法:

class Solution {
    public boolean isMatch(String s, String p) {
    //如果正则串p为空字符串s也为空这匹配成功,如果正则串p为空但是s不是空则说明匹配失败
    if (p.isEmpty())return s.isEmpty();
    //判断s和p的首字符是否匹配,注意要先判断s不为空
    boolean headMatched=!s.isEmpty()&&(s.charAt(0)==p.charAt(0)||p.charAt(0)=='.');
    if (p.length()>=2&&p.charAt(1)=='*'){//如果p的第一个元素的下一个元素是*
        //则分别对两种情况进行判断
        return isMatch(s,p.substring(2))||
            (headMatched&&isMatch(s.substring(1),p));
    }else if (headMatched){//否则,如果s和p的首字符相等
        return isMatch(s.substring(1),p.substring(1));
    }else {
        return false;
    }
}


}

方法二:动态规划:

class Solution {
    public boolean isMatch(String s, String p) {

/*
本题的dp数组的含义就是:dp[i][j]就是s的前i个元素是否可以被p的前j个元素所匹配。

我们知道了dp数组的含义之后就知道了dp数组的几个细节:

1.dp[0][0]一定是true,因为s为空且p也为空的时候一定是匹配的;2.dp[1][0]一定是false,因为s有一个字符但是p为空的时候一定是不匹配的。
3.这个boolean类型的dp数组的大小应该是dp[s.length+1][p.length+1],因为我们不仅仅要分别取出s和p的所有元素,还要表示分别取s和p的0个元素时候(都为空)的情况。
4.当写到dp[s.length][p.length]的时候,我们就得到了最终s和p的匹配情况。
5.dp[1][0]~dp[s.length][0]这一列都是false,因为s不为空但是p为空一定不能匹配。
6.所以创建好dp数组之后,初始化dp[0][0]=true、dp[0][1]=false、dp[1][0]~dp[s.length][0]都是false。然后将第一行即dp[0][2]到dp[0][p.length]的元素初始化。

7.第一行初始化思路:如果不为空的p想要匹配上为空的s,因为此时p已经不为空,则需要p是"a*"、"b*"、"c*"。。。这种形式的才能匹配上。

8.然后填写数组的其余部分,这个过程中如果p.charAt(j)=='*'依然是遵循上题中的两种情况;否则就判断两个字符串的i和j号字符是否相等,相等则分别减除当前字符继续判断,不相等则直接等于false。


*/



        //需要分别取出s和p为空的情况,所以dp数组大小+1
        boolean[][] dp=new boolean[s.length()+1][p.length()+1];
        //初始化dp[0][0]=true,dp[0][1]和dp[1][0]~dp[s.length][0]默认值为false所以不需要显式初始化
        dp[0][0]=true;
        //填写第一行dp[0][2]~dp[0][p.length]
        for (int k=2;k<=p.length();k++){
            //p字符串的第2个字符是否等于'*',此时j元素需要0个,所以s不变p减除两个字符
            dp[0][k]=p.charAt(k-1)=='*'&&dp[0][k-2];
        }
        //填写dp数组剩余部分
        for (int i=0;i<s.length();i++){
            for (int j=0;j<p.length();j++){
                //p第j个字符是否为*
                if (p.charAt(j)=='*'){
                    //两种情况:1.s不变[i+1],p移除两个元素[j+1-2]。
                    // 2.比较s的i元素和p的j-1(因为此时j元素为*)元素,相等则移除首元素[i+1-1],p不变。
                    dp[i+1][j+1]=dp[i+1][j-1]||
                        (dp[i][j+1]&&headMatched(s,p,i,j-1));
                }else {
                    //s的i元素和p的j元素是否相等,相等则移除s的i元素[i+1-1]和p的j元素[j+1-1]
                    dp[i+1][j+1]=dp[i][j]&&headMatched(s,p,i,j);
                }
            }
        }
        return dp[s.length()][p.length()];
    }
    //判断s第i个字符和p第j个字符是否匹配
    public boolean headMatched(String s,String p,int i,int j){
        return s.charAt(i)==p.charAt(j)||p.charAt(j)=='.';
    }
}

动态规划写法二:

class Solution {
    public boolean isMatch(String s, String p) {
       /*
       s和p可能为空。空的长度就是0,但是这些情况都已经判断过了,只需要判断是否为null即可
       if(p.length()==0&&s.length()==0)
            return true;
            */
        if(s==null||p==null)
            return false;
       int rows = s.length();
       int columns = p.length();
       boolean[][]dp = new boolean[rows+1][columns+1];
       //s和p两个都为空,肯定是可以匹配的,同时这里取true的原因是
       //当s=a,p=a,那么dp[1][1] = dp[0][0]。因此dp[0][0]必须为true。
       dp[0][0] = true;
        for(int j=1;j<=columns;j++)
        {   
            //p[j-1]为*可以把j-2和j-1处的字符删去,只有[0,j-3]都为true才可以
            //因此dp[j-2]也要为true,才可以说明前j个为true
            if(p.charAt(j-1)=='*'&&dp[0][j-2])
                dp[0][j] = true;
        }

        for(int i=1;i<=rows;i++)
        {
            for(int j=1;j<=columns;j++)
            {
                char nows = s.charAt(i-1);
                char nowp = p.charAt(j-1);
                if(nows==nowp)
                {
                    dp[i][j] = dp[i-1][j-1];
                }else{
                    if(nowp=='.')
                        dp[i][j] = dp[i-1][j-1];
                    else if(nowp=='*')
                    {
                        //p需要能前移1个。(当前p指向的是j-1,前移1位就是j-2,因此为j>=2)
                        if(j>=2){
                            char nowpLast = p.charAt(j-2);
                            //只有p[j-2]==s[i-1]或p[j-2]==‘.’才可以让*取1个或者多个字符:
                            if(nowpLast==nows||nowpLast=='.')
                                dp[i][j] = dp[i-1][j]||dp[i][j-1];
                            //不论p[j-2]是否等于s[i-1]都可以删除掉j-1和j-2处字符:
                            dp[i][j] = dp[i][j]||dp[i][j-2];
                        }
                    }
                    else
                        dp[i][j] = false;
                }
            }
        }
        return dp[rows][columns];
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值