题目
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
说明:
s可能为空,且只包含从a-z的小写字母。p可能为空,且只包含从a-z的小写字母,以及字符.和*。
示例 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 = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
一. 递归法
如果模式串第二位为*,则以下两种情况返回true:
*匹配0次,且匹配字符串与模式串之后所有位字符串匹配- 首位匹配,匹配字符串除去首位之后与模式串匹配
如果第二位不为*,则匹配字符串与模式串均除去首位后再比较。
js实现
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
if (!p.length) {
return s === ''
}
var first = Boolean(s) && (s[0] === p[0] || p[0] === '.')
if (p.length > 1 && p[1] === '*') {
if (isMatch(s, p.substring(2))) {
return true
}
return first && isMatch(s.substring(1), p)
} else {
return first && isMatch(s.substring(1), p.substring(1))
}
};
复杂度分析
时间复杂度:O((m+n)*2m+n/2)
空间复杂度:O((m+n)*2m+n/2)
测试结果
✔ Accepted
✔ 447/447 cases passed (136 ms)
✔ Your runtime beats 41.67 % of javascript submissions
✔ Your memory usage beats 27.53 % of javascript submissions (36.8 MB)
二. 自顶向下的动态规划
由于在匹配字符串时,我们多次匹配相同的匹配串和模式串,因此,我们使用一个二维数组来保存结果,以避免重复计算。
js实现
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
var memo = Array.from({length: s.length+1}, function() {
return new Array(p.length+1)
})
function dp(i, j) {
if (memo[i][j] === undefined) {
var ans
if (j === p.length) {
ans = i === s.length
} else {
var first = i < s.length && (p[j] === s[i] || p[j] === '.')
if (j+1 < p.length && p[j+1] === '*') {
ans = dp(i, j+2) || first && dp(i+1, j)
} else {
ans = first && dp(i+1, j+1)
}
}
memo[i][j] = ans
}
return memo[i][j]
}
return dp(0, 0)
};
复杂度分析
时间复杂度:O(mn)
空间复杂度:O(mn)
测试结果
✔ Accepted
✔ 447/447 cases passed (80 ms)
✔ Your runtime beats 98.92 % of javascript submissions
✔ Your memory usage beats 31.46 % of javascript submissions (36 MB)
三. 自低而上的动态规划
与自顶而下不同的是,我们从底部开始计算。
js实现
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
var dp = Array.from({length: s.length+1}, function() {
return new Array(p.length+1).fill(false)
})
dp[s.length][p.length] = true
for (var i=s.length;i>=0;i--) {
for (var j=p.length-1;j>=0;j--) {
var first = i < s.length && (p[j] === s[i] || p[j] === '.')
if (j+1 < p.length && p[j+1] === '*') {
dp[i][j] = dp[i][j+2] || first && dp[i+1][j]
} else {
dp[i][j] = first && dp[i+1][j+1]
}
}
}
return dp[0][0]
};
复杂度分析
时间复杂度:O(mn)
空间复杂度:O(mn)
测试结果
✔ Accepted
✔ 447/447 cases passed (76 ms)
✔ Your runtime beats 99.85 % of javascript submissions
✔ Your memory usage beats 32.02 % of javascript submissions (35.5 MB)
这篇博客详细介绍了正则表达式匹配问题的解决方法,包括递归法、自顶向下的动态规划和自底而上的动态规划。通过JavaScript实现,分析了每种方法的时间和空间复杂度,并给出了测试结果。
816

被折叠的 条评论
为什么被折叠?



