10. 正则表达式匹配

10. 正则表达式匹配

问题描述

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:

输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:

输入:
s = “aa”
p = “a*”
输出: true
解释: 因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
示例 3:

输入:
s = “ab”
p = “."
输出: true
解释: ".
” 表示可匹配零个或多个(’*’)任意字符(’.’)。
示例 4:

输入:
s = “aab”
p = “cab”
输出: true
解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。
示例 5:

输入:
s = “mississippi”
p = “misisp*.”
输出: false

解决方法:

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

如果没有星号(正则表达式中的 * ),问题会很简单——我们只需要从左到右检查匹配串 s 是否能匹配模式串 p 的每一个字符。

当模式串中有星号时,我们需要检查匹配串 s 中的不同后缀,以判断它们是否能匹配模式串剩余的部分。一个直观的解法就是用回溯的方法来体现这种关系。

在这种情况下,当回溯多个路径失败时,可能重复计算同一个路径的结果。因此可用动态规划对回溯法进行优化。

下述代码需要注意边界条件,当匹配串s检索到最后一个字母时,模式串可能不为空。

另外,下述代码也存在一定冗余,即判断匹配串中的一个字母是否与模式串中的一个字母匹配。可通过迭代调用简化代码。

//代码1:循环
class Solution1 {
    public boolean isMatch(String s, String p) {
        return match(s,p,0,0);
    }
    private boolean match(String s, String p, int l1, int l2){
        int i=l1,j=l2;
        while(i<=s.length() && j<p.length()){
            if(j==p.length()-1 || p.charAt(j+1)!='*'){
                if(i==s.length()) return false;
                if(p.charAt(j)!='.' && s.charAt(i) != p.charAt(j)){
                    return false;
                }
                i++;
                j++;
            }else{
                for(int k=i;k<=s.length();k++){
                    if(p.charAt(j)=='.' || k==i ||s.charAt(k-1) == p.charAt(j)){
                        if(match(s,p,k,j+2)){
                            return true;
                        }
                    }else{
                        break;
                    }
                }
                return false;
            }
        }
        if(i==s.length()&&j==p.length()){
            return true;
        }else{
            return false;
        }
    }
//代码2:迭代法
class Solution2 {
    public boolean isMatch(String s, String p) {
        return match(s,p,0,0);
    }
    private boolean match(String s, String p, int l1, int l2){
        if(l2 == p.length()) return l1==s.length();
        boolean firstMatch=(l1 < s.length() && (p.charAt(l2) == '.' ||s.charAt(l1) == p.charAt(l2)));
        if(l2<p.length()-1 && p.charAt(l2+1) == '*'){
            return (match(s,p,l1,l2+2) || (firstMatch && match(s,p,l1+1,l2)));
        }else{
            return (firstMatch && match(s,p,l1+1,l2+1));
        }
    }
}

解决方法2:动态规划(优化回溯法)

动态规划:使用memo保存计算过程中的中间结果。基于暴力法的基础上,我们可以实现自顶向下以及自底向上的方法。

其中,自顶向下的方法只需要判断数组是否已经计算过。如果计算过则直接读取数组。较为方便,且在该题目中,自顶向下方法优于自底向上方法。

自底向上的方法。不需要迭代函数,只需要通过循环依次计算相应的值。在实际机试时,应先推出暴力法的计算过程(通常暴力法无法通过所有测试集)。再基于暴力法推导动态规划的计算式。

class Solution3 {
    Boolean [][]memo;
    public boolean isMatch(String s, String p) {
        memo=new Boolean[s.length()+1][p.length()+1];
        return match(s,p,0,0);
    }
    private boolean match(String s, String p, int l1, int l2){
        if(memo[l1][l2]!=null) return memo[l1][l2];
        boolean res;
        if(l2 == p.length()) res=l1==s.length();
        else{
            boolean firstMatch=(l1 < s.length() && (p.charAt(l2) == '.' ||s.charAt(l1) == p.charAt(l2)));
            if(l2<p.length()-1 && p.charAt(l2+1) == '*'){
                res = (match(s,p,l1,l2+2) || (firstMatch && match(s,p,l1+1,l2)));
            }else{
                res = (firstMatch && match(s,p,l1+1,l2+1));
            }
        }
        memo[l1][l2]=res;

        return res;
    }
}

public class Solution4 {
    public boolean isMatch(String s, String p) {
        boolean [][] memo = new boolean[s.length()+1][p.length()+1];
        memo[s.length()][p.length()]=true;
        for(int i=s.length();i>=0;i--){
            for(int j=p.length()-1;j>=0;j--){
                boolean first_match=(i<s.length()&&(p.charAt(j)=='.'||s.charAt(i)==p.charAt(j)));
                    if(j+1<p.length()&&p.charAt(j+1)=='*'){
                        memo[i][j]= memo[i][j+2] || first_match && memo[i+1][j];
                    }else {
                        memo[i][j] = first_match && memo[i + 1][j + 1];
                    }
            }
        }
        return memo[0][0];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值