leetcode-10.正则表达式匹配

字符串编辑类问题


题目详情

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

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

示例1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

示例2:

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

示例3:

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

思路:

我这道题弄迷糊的一个点:
dp的下标和p s的下标不是严格对应的
dp[i][j]对应的s p末尾字符是s[i-1] p[j-1]而不是i j
弄清了这一点之后就可以分情况讨论了

我们可以使用一个二维数组 dp,其中 dp[i][j] 表示以 i 截止的字符串是否可以被以 j 截止的正则表达式匹配。根据正则表达式的不同情况,即字符、星号,点号,我们可以分情况讨论来更新 dp 数组
大概分的情况:
1.特殊情况(sp是否为null)
2.一般情况:(由正则p的末尾开始往前分析)
<1>末尾为.
<2>末尾为✳
<3>末尾不为✳
因为<3>的情况比<2>简单,所以代码中if else
的顺序是<1><3><2>

具体的实现方法和细节见代码注释

我的代码:

class Solution 
{
public:
    bool isMatch(string s, string p) 
    {
        int m = s.size(), n = p.size();
        vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false)); //初始化
/**
这里先看特殊情况: sp为两个null串 √
                 s!=null p==null ×  
                 s==null p!=null 分情况 
*/

        dp[0][0] = true;   // s p都为null 为true

        //s==null p!=null的情况存在ture,所以我们要把这种情况的dp初始化一下
        for (int i  = 1; i <= n; ++i) //根据正则表达式中的*情况来初始化dp[0]
        {
            //如果正则表达式末尾是* 
            if (p[i-1] == '*')
            dp[0][i] = dp[0][i-2];
            //那么以p[i-1]位置的匹配情况取决于p[i-3]结尾位置
            //(因为前面位置可以被*消掉从而匹配空的s)
        }
        //分析匹配(下面分析的情况s p都不为空)
        for (int i = 1; i <= m; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                //如果正则末尾是.(匹配单个字符)
                if (p[j-1] == '.')
                //因为.可以匹配任何单个字符,所以dp[i][j]取决于dp[i-1][j-1](看剩余前面的子串是否匹配)
                dp[i][j] = dp[i-1][j-1];
                //如果末尾不是*(无法重复或抵消前面字符从而影响后面)(老老实实判断)
                else if (p[j-1] != '*')
                //取决于前面子串的情况 && 末尾字符相同与否
                dp[i][j] = dp[i-1][j-1] && p[j-1] == s[i-1];
                //(末尾为*) 连p[j-2](*的前一个字符)都无法匹配s[i-1](s末尾) 
                //&& p[j-2]不为.   那么只能用*把p[j-2]抵消掉了 
                else if (p[j-2] != s[i-1] && p[j-2] != '.')
                dp[i][j] = dp[i][j-2]; //然后取决于p[j-2]前面那一段(对应的dp是[i][j-2])是不是匹配s
                else   //最复杂的一种情况
                //       *前重复一次      *前≥2次      *前0次(抵消掉)
                dp[i][j] = dp[i][j-1] || dp[i-1][j] || dp[i][j-2];
            }
        }
        return dp[m][n]; 
    }
};

这里单独解释一下最后一个else
dp[i][j-2]和dp[i][j-1]很容易理解,就是把✳前面的那个字符重复了0-1次
dp[i-1][j]可以代表✳重复了≥2次,这是为什么呢
我们不妨分析为什么要重复大于等于两次,假设是a✳ 那么我们是为了让a这个字符不止一次和s串的末尾进行匹配,我们可以理解成a※变成了a✳a 后面这个a和s的末尾匹配对应好了,然后还可以利用a✳这个正则继续匹配s剩余的子串

posted @ 2022-07-08 18:49  ggaoda  阅读(2)  评论(0编辑  收藏  举报  来源