zoukankan      html  css  js  c++  java
  • leetcode刷题-- kmp字符串匹配算法(转)

    KMP字符匹配算法

    参考这篇讲解

    例题28

    实现 strStr() 函数。

    给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回  -1。

    示例 1:
    
    输入: haystack = "hello", needle = "ll"
    输出: 2
    示例 2:
    
    输入: haystack = "aaaaa", needle = "bba"
    输出: -1
    

    就是查找子串的问题,可以用两个指针来查找。

    class Solution:
        def strStr(self, haystack: str, needle: str) -> int:
            if needle=="": return 0
            if len(needle)>len(haystack): return -1
    
            n_h = len(haystack)
            n_n = len(needle)
            i,j = 0,0
    
            while i<n_h:
                if haystack[i]==needle[j]:
                    i += 1
                    j += 1
                else:
                    i = i - j + 1
                    j = 0
                
                if j==n_n:
                    return i-j
            return -1
    

    KMP算法

    这里的dp数组只与模式串有关,也即题目里的needle有关。

    状态:

    假设pat = "ABABC",根据之前动态规划的步骤,这里将每个数字看作一个状态。dp[i][c]表示在状态i时遇到c字符的时候。

    这里的选择有:

    • 字符相等的话,例如一开始状态是0,如果遇到字符A,状态加1,状态前进一位。
    • 字符不等的话,那么状态就需要退回,或不动。

    状态转移:

    • 相等, dp[i][c] = i + 1
    • 不等, dp[i][c] = dp[x][c]

    主要理解x的意义。

    这里的x定义为i之前的那个状态,不是指i-1。例如对照代码:

    public class KMP {
        private int[][] dp;
        private String pat;
    
        public KMP(String pat) {
            this.pat = pat;
            int M = pat.length();
            // dp[状态][字符] = 下个状态
            dp = new int[M][256];
            // base case
            dp[0][pat.charAt(0)] = 1;
            // 影子状态 X 初始为 0
            int X = 0;
            // 当前状态 j 从 1 开始
            for (int j = 1; j < M; j++) {
                for (int c = 0; c < 256; c++) {
                    if (pat.charAt(j) == c) 
                        dp[j][c] = j + 1;
                    else 
                        dp[j][c] = dp[X][c];
                }
                // 更新影子状态
                X = dp[X][pat.charAt(j)];
            }
        }
    
        public int search(String txt) {...}
    }
    

    假设pat=abcabd,那么当j=3时,也就是再次扫到字符a时,x才会加一,前进一个状态。即,一开始x=0,一直到j=3,x = dp[x][char[j]],我们可知dp[0]['a']=1,所以x=1。同理j=4时,因为状态dp[1]['b']=2也就是从状态1遇到字符b就变成状态2,x=2x变成了状态2。

    那么当遇到字符d时,就能退回到状态2。再来搜索。

    public class KMP {
        private int[][] dp;
        private String pat;
    
        public KMP(String pat) {
            this.pat = pat;
            int M = pat.length();
            // dp[状态][字符] = 下个状态
            dp = new int[M][256];
            // base case
            dp[0][pat.charAt(0)] = 1;
            // 影子状态 X 初始为 0
            int X = 0;
            // 构建状态转移图(稍改的更紧凑了)
            for (int j = 1; j < M; j++) {
                for (int c = 0; c < 256; c++)
                    dp[j][c] = dp[X][c];
                dp[j][pat.charAt(j)] = j + 1;
                // 更新影子状态
                X = dp[X][pat.charAt(j)];
            }
        }
    
        public int search(String txt) {
            int M = pat.length();
            int N = txt.length();
            // pat 的初始态为 0
            int j = 0;
            for (int i = 0; i < N; i++) {
                // 计算 pat 的下一个状态
                j = dp[j][txt.charAt(i)];
                // 到达终止态,返回结果
                if (j == M) return i - M + 1;
            }
            // 没到达终止态,匹配失败
            return -1;
        }
    }
    
  • 相关阅读:
    json格式转换
    早该知道的7个JavaScript技巧
    SPFA加上SLF时判负环的条件
    HDU 4061 A Card Game
    线性筛法求素数
    STL之deque
    POJ 3219 二项式系数
    HDU 4296 Buildings
    HDU 4292 Food (成都赛区网络赛第五题,拆点网络流)
    拆点网络流(POJ3281)
  • 原文地址:https://www.cnblogs.com/ivan-blog/p/12409850.html
Copyright © 2011-2022 走看看