10. 正则表达式匹配
2022-05-01 本文已影响0人
Sun东辉
给你一个字符串 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
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
提示:
1 <= s.length <= 20
1 <= p.length <= 30
-
s
只包含从a-z
的小写字母。 -
p
只包含从a-z
的小写字母,以及字符.
和*
。 - 保证每次出现字符
*
时,前面都匹配到有效的字符
解题思路:有限状态机(正则表达式的实现)
func debug(v ...interface{}) {
log.Println(v...)
}
func toString(i interface{}) string {
switch i.(type) {
case int:
return fmt.Sprintf("%v", i)
case string:
return fmt.Sprintf("%v", i)
case bool:
return fmt.Sprintf("%v", i)
default:
return fmt.Sprintf("%p", i)
}
}
func isMatch(s string, p string) bool {
begin := new(Node)
begin.C = '>'
begin.Size = generatePattern(begin, p, 0)
debug(begin.String())
return check(begin, s, 0)
}
type Node struct {
C byte
Parent *Node
Children map[byte][]*Node
End bool
Size int
}
func (n *Node) String() string {
return n.StringLevel(0, make(map[*Node]bool))
}
func (n *Node) StringLevel(level int, finishNodes map[*Node]bool) string {
r := make([]string, 0)
if n.End {
r = append(r, fmt.Sprintf(" id%s{%v};", toString(n), string(n.C)))
} else {
r = append(r, fmt.Sprintf(" id%s(%v);", toString(n), string(n.C)))
}
finishNodes[n] = true
for k, v := range n.Children {
for _, c := range v {
if _, ok := finishNodes[c]; !ok {
r = append(r, c.StringLevel(level+1, finishNodes))
}
r = append(r, fmt.Sprintf(" id%s -- %s --> id%s;", toString(n), string(k), toString(c)))
}
}
return strings.Join(r, "\n")
}
func (n *Node) Append(c byte, child *Node) {
m := n.Children
if m == nil {
m = make(map[byte][]*Node)
n.Children = m
}
list := m[c]
if list == nil {
list = make([]*Node, 0)
}
for _, v := range list {
if v == child {
m[c] = list
return
}
}
list = append(list, child)
m[c] = list
}
func generatePattern(now *Node, str string, idx int) int {
if len(str) <= idx {
now.End = true
return now.Size
}
vnow := now
switch str[idx] {
case '*':
now.Size = 0
now.Append(now.C, now)
default:
node := new(Node)
node.C = str[idx]
now.Append(str[idx], node)
node.Parent = now
node.Size = 1
vnow = node
}
ret := generatePattern(vnow, str, idx+1)
if ret == 0 {
now.End = true
}
addParent := now
for addParent.Parent != nil {
if addParent.Size == 0 {
debug(toString(vnow), " -> ", toString(addParent.Parent))
addParent.Parent.Append(vnow.C, vnow)
addParent = addParent.Parent
} else {
break
}
}
return now.Size + ret
}
func check(now *Node, str string, idx int) bool {
if len(str) <= idx {
return now.End
}
list := now.Children['.']
for _, v := range now.Children[str[idx]] {
list = append(list, v)
}
for _, v := range list {
r := check(v, str, idx+1)
if r {
return true
}
}
return false
}
上面的实现较为复杂,还有一种执行效率更高的函数:
func isMatch(s string, p string) bool {
n := len(s)
m := len(p)
dp := make([][]bool, n + 1)
for i := range(dp) {
dp[i] = make([]bool, m + 1)
}
dp[0][0] = true
if m > 0 && n > 0 {
dp[1][1] = s[0] == p[0] || p[0] == '.'
}
for j:=2 ;j<=m; j++ {
dp[0][j] = dp[0][j-2] && p[j-1] == '*'
}
for i:=1; i<=n; i+=1 {
for j:=2; j<=m; j+=1 {
if p[j-1] != '*' {
dp[i][j] = dp[i-1][j-1] && (s[i-1] == p[j-1] || p[j-1] == '.')
} else {
dp[i][j] = dp[i][j-2] || (dp[i-1][j] && (s[i-1]==p[j-2] || p[j-2] == '.'))
}
}
}
return dp[n][m]
}
Runtime: 0 ms, faster than 100.00% of Go online submissions for Regular Expression Matching.
Memory Usage: 2.2 MB, less than 56.82% of Go online submissions for Regular Expression Matching.