动态规划(DP)解正则匹配
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:
输入: s = "aa" p = "a"
输出: false
示例 2:
输入: s = "aa" p = "a*"
输出: true
示例 3:
输入: s = "ab" p = ".*"
输出: true
示例 4:
输入: s = "aab" p = "c*a*b"
输出: true
示例 5:
输入: s = "mississippi" p = "mis*is*p*."
输出: false
我们在下文中把需要匹配的原字符串和对应的表达式分别称为原字符串s和正则串p,正则串一共会出现如下几种情况:
'ab' 直接就是具体的字符
'a*' 某个字符和'*'来表达0-n个某个字符
'.' 表示对应任意的一个字符
'.*' 表示0-n个任意一个字符
再来看dp[1][0],这个不用讨论,空匹配串肯定不能匹配非空字符串,既dp[1][0] = false。
之后我们开始讨论通用的dp[i][j]的各类情况:
如果p[j - 1] 是一个正常字符或者是'.',则直接判断对应的s[i - 1] 是否相等即可,代码的话则可以通过 s[i - 1] == p[j - 1] || p[j - 1] == '.' 来判断。
1. 如果dp[i][j - 2] 是 true 的话 那么 dp[i][j]也是 true,我们通过图示来看一下:
也就是说,如果之前的字符串和匹配串是匹配的,那么字符串不变,匹配串增加一个'X*' (X代表 任意字符或 '.'),也同样可以匹配,图示的例子表示的就是:
'AB' 可以被 'AB' 匹配,也同时可以被 'ABD*' 匹配。
2. 如果dp[i - 1][j] 是 true 并且s[i - 1] == p [j - 2]的话 那么 dp[i][j]也是 true,我们通过图示来看一下:
如上图所示,如果之前的字符串能被一个以'*'结尾的匹配串匹配,那么如果字符串增加一个与'*'前的匹配字符相同的字符(两种情况,第一种 'D' == 'D',第二种 '任意字符' == '.' ),也就是说 为什么不是拿s[i - 1] 与 s[i - 2]去匹配的原因,他存在匹配串的字符是 '.' 的可能,下面这张图说明了特殊情况:
这种特殊情况,可以通过前面说到的用当前字符串的最后一个字符与匹配串的对应字符去匹配,而不是直接比对之前的一个字符串字符。
综上所述,我们求出了一个状态转移方程:
dp[i][j] = dp[i][j - 2]
|| dp[i - 1][j] && check(i, j-1)
结合之前的各类初始化情况,我们尝试写出解题代码:
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
let sl = s.length
let pl = p.length
let dp = []
// 判断相等 a == a || a == .
function check(si, pi){
// si pi 代表第几个数字 不涉及0 开头的问题
return s[si - 1] == p[pi - 1] || p[pi - 1] == '.'
}
// dp初始化
for(let i = 0; i <= sl; i++){
dp[i] = []
}
dp[0][0] = true
for(let j = 2; j <= pl; j++){
dp[0][j] = p[j - 1] == '*' && dp[0][j - 2]
}
// dp转移方程 空字符串或空正则的情况已经在上边处理了可能为true的情况
// 利用js特性 undefined 为 false 无需显示声明
for(let i = 1; i <= sl; i++){
for(let j = 1; j <= pl; j++){
// 状态转移方程 分类讨论
if(p[j - 1] == '*'){
dp[i][j] = dp[i][j - 2]
|| dp[i - 1][j] && check(i, j-1)
}else{
// 当前正则字符不是 *
dp[i][j] = dp[i - 1][j - 1] && check(i, j)
}
}
}
console.log(dp)
return !!dp[sl][pl]
};
这道题到这里就结束了,大家可以自己练练手,最后祝大家周末愉快,因为时间原因,只能推迟到明天做一个《动态规划解题的常见题集合》,之后还是多存点货吧,不然突然有点啥事儿,就耽搁了更新。
文章涉及到源码已经在github中开源
让我知道你在看