美文网首页LeetCode
10. 正则表达式匹配

10. 正则表达式匹配

作者: Sun东辉 | 来源:发表于2022-05-01 10:05 被阅读0次

    给你一个字符串 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.

    相关文章

      网友评论

        本文标题:10. 正则表达式匹配

        本文链接:https://www.haomeiwen.com/subject/arlyyrtx.html