10. 正则表达式匹配

题目#

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 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

提示:

  • 0 <= s.length <= 20
  • 0 <= p.length <= 30
  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 .*
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

思路#

最开始我一看是实现正则表达式(的一个子集)的引擎,瞬间就想起了该构造一个状态机,但看了看题解才发现大家都是用的动态规划,于是也是用的动态规划解题。

后面大概会用有限状态自动机来再实现一遍。

代码#

动态规划#

func isMatch(s string, p string) bool {
memo := map[string]bool{}
return dp(s, 0, p, 0, memo)
}
func dp(s string, i int, p string, j int, memo map[string]bool) bool {
m := len(s)
n := len(p)
// 正则串已全部处理完成
if j == n {
return i == m
}
// 字符串本身已处理完成
if i == m {
// 正则串还剩下奇数个字符
if (n-j)%2 == 1 {
return false
}
// 检查是否是 x*y*z* 这样的形式
for ; j+1 < n; j += 2 {
if p[j+1] != '*' {
return false
}
}
return true
}
// 记录是否处理过
key := string(i) + "," + string(j)
if val, ok := memo[key]; ok {
return val
}
res := false
// . 或字面匹配
if s[i] == p[j] || p[j] == '.' {
// 有 * 通配符,可以匹配 0 次或多次
if j < n-1 && p[j+1] == '*' {
// j+2: 匹配 0 次
// i+1: 匹配多次
res = dp(s, i, p, j+2, memo) || dp(s, i+1, p, j, memo)
} else {
// 无 * 通配符,匹配 1 次
res = dp(s, i+1, p, j+1, memo)
}
} else {
// 有 * 通配符,只能匹配 0 次
if j < n-1 && p[j+1] == '*' {
return dp(s, i, p, j+2, memo)
} else {
// 无 * 通配符,匹配无法进行下去了
return false
}
}
memo[key] = res
return res
}

reference#

Last updated on