微信公众号:路人zhang
网站救助计划

1.为阅读体验,本站无任何广告,也无任何盈利方法,站长一直在用爱发电,现濒临倒闭,希望有能力的同学能帮忙分担服务器成本


2.捐助10元及以上同学,可添加站长微信lurenzhang888,备注捐助网站倒闭后可联系站长领取本站pdf内容


3.若网站能存活下来,后续将会持续更新内容

当前位置: 算法 > 剑指offer > 剑指offer 19.正则表达式匹配

题目描述

请实现一个函数用来匹配包含 .*的正则表达式。模式中的字符 . 表示任意一个字符,而 * 表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 “aaa” 与模式 “a.a” 和 “abaca” 匹配,但与 “aa.a” 和 “ab*a” 均不匹配。

示例 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 = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 ‘*’。


算法

(动态规划) O(n^2)

状态表示:s 的前 i 个字母 s[1~i] 和 p 的前 j 个字母是否匹配(下标从 1 开始比较方便 ) 状态计算:根据 p[j + 1] 是否为 * 分情况讨论,因为如果 p[j +1] = * 时那么 p[j-j+1] 是一个整体。(* 表示前面的一个字符可以出现任意多次)

特判:如果 p[j + 1] 是 * 的话那么不处理当前字符 p[j],continue,将它们作为一个整体处理,

  1. 如果当前字符 p[j] 不是 *,则 f[i][j] = true 当且仅当 s[1~i-1]p[1~j-1] 匹配且 s[i] 和 p[j] 匹配(或者 p[j] == '.' 也是匹配的)f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
  2. 如果当前字符 p[j] 是 *,则遍历 * 号前面的字符出现从 0 次到任意多次与 s[i] 进行匹配。f[i][j] = f[i][j - 2] || (i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));,这里经过了状态转移优化,这个优化类似于完全背包优化,都是用 f[i - 1][j] 替换了除出现 0 次以外的循环遍历。

时间复杂度

O(n^2)

空间复杂度

O(n^2)

C++ 代码

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;
        vector<vector<bool>> f(n + 1, vector<bool>(m + 1));

        // 两个空串匹配
        f[0][0] = true;
        // i 从 0 开始,因为 s[i] 是空串的时候可能会和 p[j] 匹配成功(当 j + 1 是 *,可以表示 p[j] 出现 0 次,此时两个串匹配成功)
        for (int i = 0; i <= n; i ++ ) {
            // j 从 1 开始,i = 0、j = 0 已经初始化过了,而对于 i != 0、j = 0,空串都不会匹配成功
            for (int j = 1; j <= m; j ++ ) {
                // 如果 p[j] 的下一个字符是 * 就不处理当前字符,因为 p[j] 和 * 在一块才能整体才能表示一个含义
                if (j + 1 <= m && p[j + 1] == '*') continue;
                // p[j + 1] 不是 *,那就看 f[i - 1][j - 1] 是否匹配且当前字符是否匹配
                if (i && p[j] != '*') {
                    f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
                } else if (p[j] == '*') {
                    // 与 0 个第 j - 1 个字母匹配 || 【1 个 || 2 个 ... 进行了状态转移优化(完全背包优化)】
                    f[i][j] = f[i][j - 2] || (i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
                }
            }
        }

        return f[n][m];
    }
};

Java 代码

public class Solution {
    public boolean isMatch(String s, String p) {
        int n = s.length(), m = p.length();
        s = " " + s;
        p = " " + p;
        boolean[][] f = new boolean[n + 1][m + 1];

        // 两个空串匹配
        f[0][0] = true;
        for (int i = 0; i <= n; i ++ ) {
            for (int j = 1; j <= m; j ++ ) {
                // 如果 p[j] 的下一个字符是 * 就不处理当前字符
                if (j + 1 <= m && p.charAt(j + 1) == '*') {
                    continue;
                }
                // p[j + 1] 不是 *,那就看 f[i - 1][j - 1] 是否匹配且当前字符是否匹配
                if (i > 0 && p.charAt(j) != '*') {
                    f[i][j] = f[i - 1][j - 1] && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.');
                } else if (p.charAt(j) == '*') {
                    // 与 0 个第 j - 1 个字母匹配 || 【1 个 || 2 个 ... 进行了状态转移优化(完全背包优化)】
                    f[i][j] = f[i][j - 2] || (i > 0 && f[i - 1][j] && (s.charAt(i) == p.charAt(j - 1) || p.charAt(j - 1) == '.'));
                }
            }
        }

        return f[n][m];
    }
}

Python 代码

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        n, m = len(s), len(p)
        s = " " + s
        p = " " + p
        f = [[False] * (m + 1) for _ in range(n + 1)]

        # 两个空串匹配
        f[0][0] = True
        for i in range(n + 1):
            for j in range(1, m + 1):
                # 如果 p[j] 的下一个字符是 * 就不处理当前字符
                if j + 1 <= m and p[j + 1] == '*':
                    continue
                # p[j + 1] 不是 *,那就看 f[i - 1][j - 1] 是否匹配且当前字符是否匹配
                if i > 0 and p[j] != '*':
                    f[i][j] = f[i - 1][j - 1] and (s[i] == p[j] or p[j] == '.')
                elif p[j] == '*':
                    # 与 0 个第 j - 1 个字母匹配 || 【1 个 || 2 个 ... 进行了状态转移优化(完全背包优化)】
                    f[i][j] = f[i][j - 2] or (i > 0 and f[i - 1][j] and (s[i] == p[j - 1] or p[j - 1] == '.'))
        
        return f[n][m]

本文由读者提供Github地址:https://github.com/tonngw


点击面试手册,获取本站面试手册PDF完整版