leetcode 10 正则表达式匹配 一道比较有意思的动态规划算法题。
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
- '.' 匹配任意单个字符
- '*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
下面简单记录下解题思路。
一、从右到左对字符串进行扫描
- base如下:
- dp[0][0]=true,代表两个空字符串能互相匹配(尽管题目中表示1 <= s.length <= 20 1 <= p.length <= 20);
2) p空则不能匹配,略过;
3) s空,p不空,则只要考虑*的情况,则dp[0][j]取决于dp[0][j-2]的值,即dp[0][j] = dp[0][j-2]
- 建立dp数组,dp[i][j] 表示s[...i]和 p[...j]能匹配;
把问题分为字符匹配和字符不匹配两种情况
- 字符匹配,则有s[i] 和 p[j] 完全匹配或者 p[j]为 . 两种情况;
2)考虑p[j]有*的情况,同样可分为字符匹配和字符不匹配两种情况:
不匹配即*匹配0次前面的字符;
匹配则又可分为*匹配0次,*匹配1次,*匹配多次.
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
let m = s.length,n = p.length
// dp[i][j] 表示s[...i]和 p[...j]能匹配
let dp = new Array(m+1).fill().map(() => new Array(n+1).fill(false))
dp[0][0] = true
// s空 p不空
for (let j = 1; j < n + 1; j++) {
if (p[j-1] === '*') {
dp[0][j] = dp[0][j-2]
}
}
for (let i = 1; i < m + 1; i++) {
for (let j = 1; j < n+1; j++) {
// 字符匹配
if (s[i-1] === p[j-1] || p[j-1] === '.') {
dp[i][j] = dp[i-1][j-1]
// p有*
} else if (p[j-1] === '*') {
if (s[i-1] === p[j-2] || p[j-2] === '.') {
// 分别匹配0/1/多次
dp[i][j] = dp[i][j-2] || dp[i-1][j-2] || dp[i-1][j]
} else {
dp[i][j] = dp[i][j-2]
}
}
}
}
return dp[m][n]
};
二、从左到右对字符串进行扫描
base如下:
1)p已经扫描完,此时如果s也刚好走完,则能匹配;
2)s已经扫描完,则此时p只有符合普通字符和成对出现的情况才能与s匹配,如 ab* ;
建立memo备忘录,memo[i][j]表示s[i...]和p[j...]能匹配。
同样把问题分为字符匹配和字符不匹配两种情况:
- 字符匹配,则有s[i] 和 p[j] 完全匹配或者 p[j]为 . 两种情况;
由于是从左到右扫描字符,此时需考虑p[j+1]为*的情况:
此时如果*匹配0个字符,则只需将p指针继续往右前进2步,s不需动,即memo[i][j] = dp(i, j+2);如果匹配多个字符,则只需将s指针向右前进一步,而p不动,即memo[i][j] = dp(i+1, j)。
2)字符不匹配,此时如果p[j+1]为*,则还能继续往下扫描;否则,则匹配失败。
var isMatch = function(s, p) {
let m = s.length,n = p.length
let memo = new Array(m).fill().map(() => new Array(n).fill())
return dp(0, 0)
// 从左到右扫描字符,若字符相同,仍需继续比对下一个字符是否有*
// dp表示s[i...]和p[j...]能匹配
function dp(i, j) {
// p走完,字符刚好匹配完
if (j === n) return i === m
// s走完,则p需符合普通字符和*成对出现才行,如 a*b*
if (i === m) {
if ((n-j) % 2 === 1) return false
for (;j < n - 1; j+=2) {
if (p[j+1] !== '*') return false
}
return true
}
if (memo[i][j] !== undefined) return memo[i][j]
if (s[i] === p[j] || p[j] === '.') {
// 还需看字符下一位是否有*
if (p[j+1] === '*') {
memo[i][j] = dp(i, j+2) || dp(i+1, j)
} else {
memo[i][j] = dp(i+1, j+1)
}
} else {
if (p[j+1] === '*') {
memo[i][j] = dp(i, j+2)
} else {
memo[i][j] = false
}
}
return memo[i][j]
}
};