0010. Regular Expression Matching (H)

Regular Expression Matching (H)

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

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

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

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like . or *.

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 = "c*a*b"
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 = "mis*is*p*."
Output: false

题意

实现一个包含'.'、'*'的正则匹配判定。

思路

递归法:

递归边界为,当p空时,若s非空则返回false,若s空则返回true。(反过来s空p非空时无法作为边界,因为如果s为空,p为 ".*",则同样满足匹配)。

当p的第二位不为 '*' 时,如果当前s和p的第一位匹配,那么只要递归判断isMatch(s.subString(1), p.subString(1))即可;
当p的第二位为 '*' 时,说明要对p的第一位字符进行多次匹配判断,这里可以分为两种情况 (记 '*' 前字符为x):1. s的第一位与x匹配,递归判断s第一位之后的子字符串是否与p匹配 (因为 "x*" 表示x可以出现0次或多次,所以每次递归可以理解为去除了一个匹配的x);2. s的第一位与x不匹配,但与 '*' 后的第一个字符匹配,或者s的第一位既与x匹配,也与 '*' 后的第一个字符匹配,那么要判断s是否与 "x*" 之后的正则表达式相匹配。以上两种情况满足其一即可说明匹配成功。

记忆化搜索:

可以看到,在递归的过程中存在着许多重复的计算(即相同的s和p子串出现了多次),因此可以用一个数组将中间状态保存下来。

动态规划:

参考 [LeetCode] 10. Regular Expression Matching 正则表达式匹配


代码实现

Java

递归

class Solution {
    public boolean isMatch(String s, String p) {
        // 递归边界,只能以p空为判断点
        if (p.isEmpty()) {
            return s.isEmpty();
        } 

        boolean isFirstMatch = (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'));
        if (p.length() >= 2 && p.charAt(1) == '*') {
            return ((isFirstMatch && isMatch(s.substring(1), p)) || isMatch(s, p.substring(2)));
        } else {
            return isFirstMatch && isMatch(s.substring(1), p.substring(1));
        }
    }
}

记忆化搜索优化

class Solution {
    public boolean isMatch(String s, String p) {
        return isMatch(s, 0, p, 0, new int[s.length() + 1][p.length() + 1]);
    }

    private boolean isMatch(String s, int sHead, String p, int pHead, int[][] record) {
        if (pHead == p.length()) {
            return sHead == s.length();
        }

        if (record[sHead][pHead] != 0) {
            return record[sHead][pHead] == 1 ? true : false;
        }

        boolean matched = false;
        boolean firstMatch = sHead < s.length() && (s.charAt(sHead) == p.charAt(pHead) || p.charAt(pHead) == '.');
        if (pHead < p.length() - 1 && p.charAt(pHead + 1) == '*') {
            matched = firstMatch && isMatch(s, sHead + 1, p, pHead, record) || isMatch(s, sHead, p, pHead + 2, record);
        } else {
            matched = firstMatch && isMatch(s, sHead + 1, p, pHead + 1, record);
        }

        record[sHead][pHead] = matched ? 1 : -1;
        return matched;
    }
}

动态规划

class Solution {
    public boolean isMatch(String s, String p) {
        boolean dp[][] = new boolean[s.length() + 1][p.length() + 1];
        dp[0][0] = true;

        for (int i = 0; i <= s.length(); i++) {
            for (int j = 1; j <= p.length(); j++) {
                if (j > 1 && p.charAt(j - 1) == '*') {
                    dp[i][j] = dp[i][j - 2] || i > 0 && dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.');
                } else {
                    dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.');
                }
            }
        }

        return dp[s.length()][p.length()];
    }
}

JavaScript

/**
 * @param {string} s
 * @param {string} p
 * @return {boolean}
 */
var isMatch = function (s, p) {
  if (p.length === 0) {
    return s.length === 0
  }

  let firstMatch = s.length !== 0 && (s[0] === p[0] || p[0] === '.')

  if (p.length >= 2 && p[1] === '*') {
    return (firstMatch && isMatch(s.slice(1), p)) || isMatch(s, p.slice(2))
  } else {
    return firstMatch && isMatch(s.slice(1), p.slice(1))
  }
}
posted @ 2020-06-19 06:54  墨云黑  阅读(104)  评论(0编辑  收藏  举报