LeetCode.10 Regular Expression Matching (正则表达式匹配)

本文介绍了一种使用动态规划方法实现正则表达式匹配的高效算法,支持'.'和'*'特殊字符,并通过具体实例详细解析了算法的运行过程。

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

题目:


Implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

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", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

分析:

class Solution {
    public boolean isMatch(String s, String p) {
        /**思路
这道题可以用递归解决,不过时间复杂度是指数级,这里介绍一个用动态规划在平方时间内解决的办法。
解法的核心理念是:从后往前看pattern的每一位,对于pattern的每一位,我们尽可能的把待匹配串string从后往前给匹配上。我们用一个数组match[string.length() + 1]来表示string被匹配的情况,这里如果match[j]是true,而我们pattern正指向第i位,则说明string从第j位到最后都已经被pattern第i位之前的某些部分给成功匹配了,所以我们不用再操心了。match[i]为true的条件是match[i + 1]为true,且string第i个字符也能被成功匹配。

那我们就可以从后往前开始看pattern的每一位能匹配多少string的字符了:

如果pattern的这一位是*,那我们要用这一位,来从后往前尝试匹配string,因为string后面是已经匹配好的,前面是还没匹配好的,所以从前往后匹配星号可能会导致我们匹配了一些pattern该星号前面的星号应该匹配的部分。而从后往前匹配则不会影响pattern该星号后面星号所匹配的部分,因为已经匹配的部分我们会直接跳过。
如果pattern这一位不是*,那我们则不能匹配多个字符,我们只能匹配一个字符,这时候要对string从前往后匹配,因为如果后面没被匹配,前面也肯定不会被匹配,所以从前向后能保证我们把pattern的这一位匹配到string当前最后面那个还没匹配的字符。这样如果那个字符能被匹配就通过了。
我们举个例子

match:   0 0 0 1
string:  a a b
pattern: a * b
             |
这里我们先看pattern最后一位b能匹配到多少,这里因为b不是星号,所以我们从左往右尝试匹配string,第一个a不行,第二个a也不行,然后到b,这里因为match[3]是true,b也和b相同,所以匹配成功。

match:   0 0 1 1
string:  a a b
pattern: a * b
           |
然后看pattern的这个星号,我们要从后往前匹配string。因为b已经被匹配了,match[2]是true,所以直接跳过。然后到a,发现个pattern中星号前面的字符a相同,所以匹配成功,match[1]也置为true再看string的第一个a,还是可以匹配成功,这样整个string都被匹配成功了。

这里还有几个情况,首先,无论刚才那pattern中最后一个b有没有匹配到string中任何一个字符,match[3]也要置为false。这样才能防止pattern最后字母没有匹配上,而pattern前面的部分反而把string的结尾给匹配了。还有如果pattern中是句号的话,那相当于字符相同。
*/
        boolean [] flag=new boolean[s.length()+1];
        flag[s.length()]=true;
        
        for(int i=p.length()-1;i>=0;i--){
            if(p.charAt(i)=='*'){
                //如果是星号,s从后往前
                for(int j=s.length()-1;j>=0;j--){
                    flag[j]=flag[j]||(flag[j+1]&&(p.charAt(i-1)==s.charAt(j)||p.charAt(i-1)=='.'));
                }
                //记得把i多减1,因为*和其前面的字符是一起使用的
                i--;
            }else{
                //不是星号,s从前往后
                for(int j=0;j<s.length();j++){
                    flag[j]=flag[j+1]&&(p.charAt(i)=='.'||p.charAt(i)==s.charAt(j));
                }
                // 只要试过了pattern中最后一个字符,就要把match[s.length()]置为false
                flag[s.length()] = false;
            }
        }
        
        return flag[0];
    }
}

剑指offer:

public class Solution {
    public boolean match(char[] str, char[] pattern)
    {
        //正则表达式
        if(str==null||pattern==null){
            return false;
        }
        return matchCore(str,0,pattern,0);
    }
    public boolean matchCore(char [] str,int indexI,char [] pattern ,int indexJ){
         //两个均到末尾了
        if(indexI==str.length&&indexJ==pattern.length){
            return true;
        }
        
        //str没有到末尾,pattern到末尾了
        if(indexI!=str.length&&indexJ==pattern.length){
            return false;
        }
        //str到末尾了,pattern没有到末尾(不一定匹配失败,因为a*可以匹配0个字符)
        if(indexI==str.length&&indexJ!=pattern.length){
            //只有pattern剩下部分
            if(indexJ+1<pattern.length&&pattern[indexJ+1]=='*'){
                //匹配0个,跳过
                return matchCore(str,indexI,pattern,indexJ+2);
            }
            return false;
        }
        //str没有到末尾了,pattern未到末尾
        if(indexJ+1<pattern.length&&pattern[indexJ+1]=='*'){
            //后面一个为*需要考虑
            if(str[indexI]==pattern[indexJ]||(pattern[indexJ]=='.'&&indexI!=str.length)){
                //*匹配一个,跳过
                //*匹配一个,再匹配str中的下一个
                 //*匹配0个,跳过
                return matchCore(str,indexI+1,pattern,indexJ+2)||matchCore(str,indexI+1,pattern,indexJ)||matchCore(str,indexI,pattern,indexJ+2);
            }else{
                //直接跳过*(*匹配到0个)
                return matchCore(str,indexI,pattern,indexJ+2);
            }
        }
        //相同的情况下,都往后走一个
        if(str[indexI]==pattern[indexJ]||(pattern[indexJ]=='.'&&indexI!=str.length)){
            return matchCore(str,indexI+1,pattern,indexJ+1);
        }
        return false;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值