题目描述
请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。
示例1
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例2
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例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
Leetcode链接:剑指offer面试题19:正则表达式匹配
算法分析
- 首先想到的是回溯法,其实就是直接分析每一个状态,从后向前匹配模式串(当前字符:pc)和主串(当前字符:sc)。如果pc == sc,或者pc == “.”,则匹配成功,直接跳过当前字符;若pc == "“时,就要考虑”?"重复0次,1次或者多次的情况。
递归的终止条件是其中一个字符串已经遍历完,此时若另一个字符串也已经遍历完,说明模式串可以匹配上主串,return true;如果模式串遍历结束,主串尚未遍历完,说明不能匹配,return false;最后是主串遍历结束,模式串尚未遍历完的情况,这种情况比较复杂,详见注释。具体算法为(isMatch1()) - 后来参考了leetcode的官方题解,完成了动态规划求解。具体算法为(isMatch2())
要注意的有两个点,一个是dp记录s[:n] 和 p[:m]能否匹配,所以字符串实际索引范围应该为1~len;另一点是为了解决越界问题,双重循环中j从1开始(dp[i][0]必定为false)。
Golang代码如下
func backtrack19(s, p string, i, j int) bool {
if i < 0 || j < 0 {
if i < 0 && j < 0 {
return true
}
if j < 0 {
return false
}
if p[j] == '*' {
// 如果模式串当前字符不是"*",则不可能匹配成功
for index := j; index >= 0; index = index - 2 {
// 判断模式串尚未遍历完的部分是不是都是"?*"
if p[index] != '*' {
// 不满足匹配成功的条件
return false
}
}
return true
}
return false
} else {
if s[i] == p[j] || p[j] == '.' {
return backtrack19(s, p, i-1, j-1)
}
if p[j] == '*' {
if p[j-1] != s[i] && p[j-1] != '.' {
return backtrack19(s, p, i, j-2)
}
// 考虑模板串"?*"重复0次,1次或者多次的情况
return backtrack19(s, p, i-1, j) || backtrack19(s, p, i-1, j-2) || backtrack19(s, p, i, j-2)
}
}
return false
}
func isMatch1(s string, p string) bool {
// 判空
if len(p) == 0 {
return len(s) == 0
}
return backtrack19(s, p, len(s)-1, len(p)-1)
}
func isMatch2(s string, p string) bool {
if len(p) == 0 {
return len(s) == 0
}
matches := func (i, j int) bool {
if i == 0 {
return false
}
if s[i-1] == p[j-1] || p[j-1] == '.' {
return true
}
return false
}
// dp记录s[:n] 和 p[:m]能否匹配
dp := make([][]bool, len(s)+1)
for i := 0; i <= len(s); i++ {
dp[i] = make([]bool, len(p)+1)
}
dp[0][0] = true
for i := 0; i <= len(s); i++ {
// i > 0 && j == 0 不可能匹配成功
for j := 1; j <= len(p); j++ {
if p[j-1] == '*' {
// 无论是否matches(i, j-1),都要考虑"?*"匹配次数为0的情况
dp[i][j] = dp[i][j-2]
if matches(i, j-1) {
// "?*"匹配次数加一
dp[i][j] = dp[i][j] || dp[i-1][j]
}
} else if matches(i, j) {
dp[i][j] = dp[i-1][j-1]
}
}
}
return dp[len(s)][len(p)]
}