“Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。”
一、题目描述:
正则表达式匹配
请实现一个函数用来匹配包含'. '和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。
示例 1:
输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。 示例 2:
输入: s = "aa" p = "a*" 输出: true 解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。 示例 3:
输入: s = "ab" p = "." 输出: true 解释: "." 表示可匹配零个或多个('*')任意字符('.')。 示例 4:
输入: s = "aab" p = "cab" 输出: true 解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。 示例 5:
输入: s = "mississippi" p = "misisp*." 输出: false s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 ,无连续的 ''。
二、思路分析:
用二维动态规划数组dp[i][j]表示s的前i位于p的前j位是否匹配,sl表示s的长度,pl表示p的长度,对于该题目返回dp[sl][pl]判断是否匹配
对于dp[i][j],当p[j]不是* 时,判断s[i]===p[j]是否相等,若不相等肯定为false,相等则值为dp[i-1][j-1]值,同时注意判断p[j]是.的特殊情况也是等同于s[i]===p[j]
当p[j]是* 时,就要判断s[i]===p[j-1]?当不匹配上时忽略这两项,即值为dp[i][j-2],当匹配上时因为会多次出现p[j-1],故而值为dp[i-1][j]而我们也可以选择不匹配。故 dp[i][j]=dp[i-1][j]||dp[i][j-2]
那么我们动态规划的起点为dp[0][0]=true即都为空字符时为true,这样之前的原理的索引都要加上1。又因为s为空字符而当p包含* 时不为空也可能匹配上,故对于s的字符遍历要从空字符开始
另外一个题外话,js创建类似动态规划二维数值时子数组要避免是浅复制,要是浅复制只复制地址则每改变一个值都会引起变化
三、AC代码
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
let sl=s.length+1,pl=p.length+1;
let dp=new Array(sl).fill(0).map(()=>new Array(pl).fill(false));
dp[0][0]=true;
for(let i=0;i<sl;i++){
for(let j=1;j<pl;j++){
if(p[j-1]==="*"){
dp[i][j]=dp[i][j-2]
if(i>0&&(s[i-1]===p[j-2]||p[j-2]===".") ){
dp[i][j]=dp[i-1][j]||dp[i][j]
}
}else{
if(i>0&&(p[j-1]==="."||p[j-1]===s[i-1])){
dp[i][j]=dp[i-1][j-1]
}
}
}
}
return dp[sl-1][pl-1]
};
四、总结
详细分析推导递归公式要考虑的条件,同时编写时记得边界判断