[LeetCode 10.] 正则表达式匹配【hard】

LeetCode 10. 正则表达式匹配

这道题是正则表达式的匹配,初始化和状态转移都比较繁琐,需要非常小心,测试样例也给的比较全一些。先做一道简化版的 LeetCode 44. 通配符匹配 会简单一些。

题目描述

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*' where:

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

The matching should cover the entire input string (not partial).

Example 1:

Input: s = "aa", p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

Example 2:

Input: s = "aa", p = "a"
Output: true
Explanation: '
' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

Input: s = "ab", p = "."
Output: true
Explanation: ".
" means "zero or more (*) of any character (.)".

Example 4:

Input: s = "aab", p = "cab"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".

Example 5:

Input: s = "mississippi", p = "misisp*."
Output: false

Constraints:

  • 0 <= s.length <= 20
  • 0 <= p.length <= 30
  • s contains only lowercase English letters.
  • p contains only lowercase English letters, '.', and '*'.
  • It is guaranteed for each appearance of the character '*', there will be a previous valid character to match.

解题思路

这种两个字符串的问题,绝大多数都直接 DP,开个二位数组来做。
我们定义 dp[i][j] 表示 s[:i]p[:j] 是否匹配,则 dp[len(s)][len(p)] 就是最终要求的答案。

我们在每一维多开一个元素的空间,用于表示字符串为空的时候的匹配。初始化的时候需要考虑的情况:

  • 若 s 和 p 都为空,dp[0][0] 为 true;
  • 若 s 为空、p 非空,dp[0][j] 可能 true 可能 false,参考 Example 4,当 p 为连续的 "a" 或者 "." 的时候为 true;
  • 若 s 非空、p 为空,dp[i][0] 为 false;

当 s 和 p 都非空的时候,开始状态转移:

  • 若 sc == pc,s 和 p 往前看一位即可,dp[i][j] = dp[i-1][j-1];
  • 若 pc == '.',当前最后一位仍然匹配,dp[i][j] = dp[i-1][j-1];
  • 若 pc == '',则需要考察前一位的情况,'' 可以匹配 0 位、1 位、或多位:
    • 若 ppc == sc 或者 ppc == '.',以下条件满足其一即可匹配:
      • "a" 或者 "." 匹配 0 次;
      • "a" 或者 "." 匹配 "a" 1 次;
      • "a" 或者 "." 匹配 "aaa" 多次;
    • 若 ppc != sc,只能匹配 0 位,dp[i][j] = dp[i][j-2];

条件很多很复杂,需要一条一条仔细实现。尤其是匹配多次的情况,可以通过递归推导来实现:若 s[:i] 与 p[:j] 可以匹配2次及以上,则 s[:i-1] 与 p[:j] 也是匹配的。

参考代码

/*
 * @lc app=leetcode id=10 lang=cpp
 *
 * [10] Regular Expression Matching
 */

// @lc code=start
class Solution {
public:
    bool isMatch(string s, string p) {
        int ns = s.size();
        int np = p.size();

        // initialize dp[][]
        vector<vector<bool>> dp(ns+1, vector<bool>(np+1));
        dp[0][0] = true; // empty string match empty pattern
        for (int i=1; i<=ns; i++) {
            dp[i][0] = false; // non-empty string match empty pattern
        }
        for (int j=2; j<=np; j+=2) {
            if (p[j-1] == '*') {
                dp[0][j] = dp[0][j-2]; // empty string match non-empty pattern
            } else {
                break;
            }
        } // case "aab" "c*a*b"

        for (int i=1; i<=ns; i++) {
            for (int j=1; j<=np; j++) {
                if (s[i-1] == p[j-1] || p[j-1] == '.') {
                    dp[i][j] = dp[i-1][j-1];
                } else if (p[j-1] == '*') {
                    if (s[i-1] == p[j-2] || p[j-2] == '.') {
                        // match 0, 1, or n char
                        dp[i][j] = dp[i][j-2] || dp[i][j-1] || dp[i-1][j];
                    } else {
                        dp[i][j] = dp[i][j-2];
                    }
                } else {
                    dp[i][j] = false;
                }
            }
        }
        return dp[ns][np];
    }
};
// @lc code=end

其他解法

这篇博客 提供了两种简洁的递归解法,可以对比参考。

原文地址:https://www.cnblogs.com/zhcpku/p/14473292.html