LeetCode 10 正则表达式匹配

题目链接:LeetCode 10 正则表达式匹配

题目大意:
给你一个字符串\(s\)和一个字符规律\(p\),请你来实现一个支持\('.'\)\('*'\)的正则表达式匹配。

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

所谓匹配,是要涵盖整个字符串\(s\)的,而不是部分字符串。

题解:
参考自LeedCode官方题解
题目中的匹配是一个“逐步匹配”的过程:我们每次从字符串\(p\)中取出一个字符或者“字符+星号”的组合,并在\(s\)中进行匹配。对于\(p\)中一个字符而言,它只能在\(s\)中匹配一个字符,匹配的方法具有唯一性;而对于\(p\)中“字符+星号”的组合而言,它可以在\(s\)中匹配任意自然数个字符,并不具有唯一性。因此我们可以考虑使用动态规划,对匹配的方案进行枚举。

我们用\(f[i][j]\)表示\(s\)的前\(i\)个字符与\(p\)中的前\(j\)个字符是否能够匹配。在进行状态转移时,我们考虑\(p\)的第\(j\)个字符的匹配情况:

  • 如果\(p\)的第\(j\)个字符是一个小写字母,那么我们必须在\(s\)中匹配一个相同的小写字母,即

\[f[i][j]= \left\{ \begin{array}{l} f[i-1][j-1],&s[i]=p[j] \\ false,&s[i]\neq p[j] \end{array} \right. \]

也就是说,如果\(s\)的第\(i\)个字符与\(p\)的第\(j\)个字符不相同,那么无法进行匹配;否则我们可以匹配两个字符串的最后一个字符,完整的匹配结果取决于两个字符串前面的部分。

  • 如果\(p\)的第\(j\)个字符是\('*'\),那么就表示我们可以对\(p\)的第\(j-1\)个字符匹配任意自然数次。在匹配\(0\)次的情况下,我们有

\[f[i][j]=f[i][j−2] \]

也就是我们“浪费”了一个“字符+星号”的组合,没有匹配任何\(s\)中的字符。
在匹配\(1,2,3,\cdots\)次的情况下,类似地我们有

\[\begin{aligned} &f[i][j]=f[i−1][j−2],\quad \ if\ \ s[i]=p[j−1] \\ &f[i][j]=f[i−2][j−2],\quad \ if\ \ s[i−1]=s[i]=p[j−1] \\ &f[i][j]=f[i−3][j−2],\quad \ if\ \ s[i−2]=s[i−1]=s[i]=p[j−1] \\ &\cdots\cdots \end{aligned} \]

如果我们通过这种方法进行转移,那么我们就需要枚举这个组合到底匹配了\(s\)中的几个字符,会增导致时间复杂度增加,并且代码编写起来十分麻烦。我们不妨换个角度考虑这个问题:“字母+星号”的组合在匹配的过程中,本质上只会有两种情况:

  1. 匹配\(s\)末尾的一个字符,将该字符扔掉,而该组合还可以继续进行匹配;
  2. 不匹配字符,将该组合扔掉,不再进行匹配。

如果按照这个角度进行思考,我们可以写出很精巧的状态转移方程:

\[f[i][j]= \left\{ \begin{array}{l} f[i-1][j]\ or\ f[i][j-2],&s[i]=p[j-1] \\ f[i][j-2],&s[i]\neq p[j-1] \end{array} \right. \]

  • 在任意情况下,只要\(p[j]\)\('.'\),那么\(p[j]\)一定成功匹配\(s\)中的任意一个小写字母。

最终的状态转移方程如下:

\[f[i][j]= \left\{ \begin{array}{l} if\ \ p[j]\neq '*' \left\{ \begin{array}{l} f[i-1][j-1],&matches(s[i],p[j]) \\ false,&otherwise \end{array} \right. \\ otherwise \left\{ \begin{array}{l} f[i-1][j]\ or\ f[i][j-2],&matches(s[i],p[j-1]) \\ f[i][j-2],&otherwise \end{array} \right. \end{array} \right. \]

其中\(matches(x,y)\)是判断两个字符是否匹配的辅助函数。只有当\(y\)\('.'\)或者\(x\)\(y\)本身相同时,这两个字符才会匹配。

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size();
        int n = p.size();
        auto matches = [&](int i, int j) {
            return i && (p[j - 1] == '.' || s[i - 1] == p[j - 1]);
        };
        vector<vector<int>> f(m + 1, vector<int>(n + 1));
        f[0][0] = true;
        for (int i = 0; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (p[j - 1] == '*') {
                    f[i][j] |= f[i][j - 2];
                    if (matches(i, j - 1)) {
                        f[i][j] |= f[i - 1][j];
                    }
                }
                else {
                    if (matches(i, j)) {
                        f[i][j] |= f[i - 1][j - 1];
                    }
                }
            }
        }
        return f[m][n];
    }
};
posted @ 2022-02-11 21:02  ZZHHOOUU  阅读(31)  评论(0编辑  收藏  举报