• 正则表达式匹配与自动机


    自动机的核心就是“状态”和“状态转移”,所以自动机又叫状态机。而动态规划也恰好是“状态”与”状态转移“。自动机又分为DFA与NFA,DFA一个输入对应一个状态转移,转移过程是确定的,而NFA一个状态输入对应多个转移方程

    在LeetCode正则表达式匹配这道困难题目中,很明显*号对应着一个NFA,初始的状态为*号前面的字符,输入状态为*号,状态的转移分为匹配该字符零次和匹配该字符n次(n>=1)时text和pattern的移动位置,把匹配零次等同于状态机中的跳空,初始状态及该状态跳空即匹配零次的集合还能再接受pattern(i) == text(j)或者pattern(i)==.的输入,即

     子集A为匹配零次即置空的状态,该子集接受两个输入称为a和b,得到子集B和子集C,再以此由B和C继续接受输入,依次循环。

    而当*号匹配当前字符n次时,也同样可以抽象出一个DFA的初始状态,即当前字符相等时,进入text(i+1)&&pattern(j)的状态,接下来步骤跟匹配零次差不多。

    最后合并*号所有的情况就是,该匹配算法是个接受三个初始状态的nfa,当前pattern(i)==text(j)||pattern(i)==.,即算法中的first_match变量,该变量的两个状态再加上第二位是否有*号的状态判断,可知总共有三个初始状态

    所以该nfa转为dfa,该dfa的初态就要包含这三个状态,终态和nfa一样,都即pattern和text同时为空时。

    下面为递归代码,可以说模拟了nfa的匹配过程

    public boolean isMatch(String text, String pattern) {
            //递归回溯结束点
            if (pattern.isEmpty()) {
                return text.isEmpty();
            }
            boolean first_match = (!text.isEmpty() &&
                    (pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '.'));
    
            //如果pattern长度大于2并且第二位是*,这一步isMatch3(text, pattern.substring(2)可以判定用到*匹配零个前字符isMatch3(text, pattern.substring(2)
            if (pattern.length() >= 2 && pattern.charAt(1) == '*') {
                //这个匹配判定用到*匹配零个前字符
                return (isMatch3(text, pattern.substring(2)) ||
                        //这个匹配判定*代表n个字符
                        (first_match && isMatch3(text.substring(1), pattern)));
            } else {
                //判定第二位不是*的情况都移位判定,每回递归只判断第一位,直到空
                return first_match && isMatch3(text.substring(1), pattern.substring(1));
            }
        }
    

      下面是动态规划解法

    class Solution {
        public boolean isMatch(String s, String p) {
            int ls = s.length(), lp = p.length();
            boolean[][] dp = new boolean[ls + 1][lp + 1];
            dp[0][0] = true;
            for(int j = 2; j <= lp; j++)
                dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
            for(int i = 1; i <= ls; i++) {
                for(int j = 1; j <= lp; j++) {
                    int m = i - 1, n = j - 1;
                    if(p.charAt(n) == '*')
                        dp[i][j] = dp[i][j - 2] || dp[i - 1][j] && 
                            (s.charAt(m) == p.charAt(n - 1) || p.charAt(n - 1) == '.');
                    else if(s.charAt(m) == p.charAt(n) || p.charAt(n) == '.') 
                        dp[i][j] = dp[i - 1][j - 1];
                }
            }
            return dp[ls][lp];
        }
    }
  • 相关阅读:
    再次或多次格式化导致namenode的ClusterID和datanode的ClusterID之间不一致的问题解决办法
    Linux安装aria2
    POJ 3335 Rotating Scoreboard 半平面交
    hdu 1540 Tunnel Warfare 线段树 区间合并
    hdu 3397 Sequence operation 线段树 区间更新 区间合并
    hud 3308 LCIS 线段树 区间合并
    POJ 3667 Hotel 线段树 区间合并
    POJ 2528 Mayor's posters 贴海报 线段树 区间更新
    POJ 2299 Ultra-QuickSort 求逆序数 线段树或树状数组 离散化
    POJ 3468 A Simple Problem with Integers 线段树成段更新
  • 原文地址:https://www.cnblogs.com/moonyaoo/p/12834067.html
Copyright © 2020-2023  润新知