LEECODE No.10
先上题:
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
借鉴之前一个老哥的方法分三步:
- 定义dp数组的含义
- 找到dp数组的递推关系(最优子结构)
- 找dp数组初始值
1.首先定义dp[i][j]数组含义
dp[i][j]表示 s 的前 i 个和 p 的前 j 个能匹配,为布尔值。
2.确定状态转移方程
从dp[i-1][j-1]入手,首先分为两种情况:
- P[j]=s[i]:此时由dp[i-1][j-1] => dp[i][j]
- P[j]!=s[i]:此时分为两种情况
- P[j]="." :此时可代表任意一个字符,可由dp[i-1][j-1] => dp[i][j]
- P[j]="*" :此时是否匹配要看P[j-1],分两种情况如下
- P[j-1]!=s[i] :由dp[i][j-2] => dp[i][j],例如 S ____a
P ____b* 即____a和____必须匹配,b*匹配成零个元素
- P[j-1]=s[i] or P[j-1]="." dp[i−1][j] => dp[i][j]dp[i][j-2] => dp[i][j]
3. 确定dp矩阵的初始值和边界
需要注意dp矩阵需要大一圈来储存S或P为空的情况
i>=1, j>=1
初始值dp[0][0]=true, dp[i][0]=false, dp[0][j]情况有两种 a. j=1 || p[j-1] !=" * " => dp[0][j] = false
b.反之 j != 1 && p[j-1] = " * " => dp[0][j] = dp [0][j-2]
附代码如下:
/**
* @param {string} s
* @param {string} p
* @returns {string} boolean
*
*/
let isMatch = (s, p) => {
//初始化dp数组,须比s,p大一圈用来储存s或p为空的情况
//dp[i][j]表示 s 的前 i 个和 p 的前 j 个能匹配
let dp = Array(s.length+1);
for (let i=0; i<dp.length; i++){
//初始化包含dp[i][0]=false这种情况,后面无需再赋值
dp[i] = Array(s.length+1).fill(false);
}
dp[0][0] = true;
//考虑dp[0][j]有两种情况
for (let j=1; j<p.length+1; j++){
//j = 1 || p[j−1] != "∗"
if(j == 1 || p.charAt(j-1)) dp[0][j] = false;
//j != 1 && p[j−1] = "∗"
else dp[0][j] = dp[0][j-2];
}
//分情况讨论
for (let i=1; i<=s.length; i++){ //最外围不用
for(let j=1; j<=p.length; j++){
if(p.charAt(j) === '.' || p.charAt(j) === s.charAt(i)){
dp[i][j] = dp[i-1][j-1];
}
if(p.charAt(j) == '*'){
if(p.charAt(j-1) != s.charAt(i)){
//考虑边界防止数组溢出
if(j == 1) dp[i][j] = false;
else dp[i][j] = dp[i][j-2];
}else if(p.charAt(j-1) == s.charAt(i) || p.charAt(j-1) == '.'){
dp[i][j] = dp[i-1][j] || dp[i][j-2];
}
}
}
}
return dp[s.length][p.length];
};