LeetCode28 KMP算法

labuladong题解参考

重点记录

dp[j][c] = next
0 <= j < M,代表当前的状态
0 <= c < 256,代表遇到的字符(ASCII 码)
0 <= next <= M,代表下一个状态

dp[4]['A'] = 3 表示:
当前是状态 4,如果遇到字符 A,
pat 应该转移到状态 3

dp[1]['B'] = 2 表示:
当前是状态 1,如果遇到字符 B,
pat 应该转移到状态 2

search方法,用于根据dp数组查找txt字符串中是否存在目标子串
KMP 算法永不回退 txt 的指针 i,不走回头路(不会重复扫描 txt),而是借助 dp 数组中储存的信息把 pat 移到正确的位置继续匹配,时间复杂度只需 O(N)。

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++) {
        // 当前是状态 j,遇到字符 txt[i],
        // pat 应该转移到哪个状态?
        j = dp[j][txt.charAt(i)];
        // 如果达到终止态,返回匹配开头的索引
        if (j == M) return i - M + 1;
    }
    // 没到达终止态,匹配失败
    return -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)];
// dp[x][pat.charAt(j)]只有两种结果,一是X前进一位,例如ABCABC中B->C
// 另一个结果是回退,例如ABCABDCBCB中 charAt(j)=D,此时X=2-->X=0      
        }
    }

    public int search(String txt) {...}
}


作者:labuladong
链接:https://leetcode-cn.com/problems/implement-strstr/solution/kmp-suan-fa-xiang-jie-by-labuladong/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于影子状态X:虽然不能都匹配上, 但截掉前面部分,后面还可以部分匹配上
影子状态的前缀:从0下标处到影子状态X处, 当前位置的前缀
: 当前位置J到影子状态X之间 两个前缀要吻合,J退后一个状态身位,截取影子状态的前缀 匹配当前的被匹配字符串。
ABABA… -------> 母串
ABABC ------> 模式串,
(如果模式串是CABABC,母串是CABABA?)
当要匹配的是 C 和 A 时,X = 2,恰巧母串为A,此时可将模式串匹配位置后移一个状态,如果母串为D,模式串匹配位置则继续回退到X=0处

LeetCode 28

class Solution {
    public int strStr(String haystack, String needle) {
        // return haystack.indexOf(needle);
        int N = needle.length();
        if (N == 0) return 0;
        int[][] dp = new int[N][256];
        KMP(needle, dp);
        return search(haystack,dp);
    }
    public void KMP(String pattern, int[][] dp) {
        // 初始化0状态处值,除了下面情况为1,其他默认都是0
        dp[0][pattern.charAt(0)] = 1;
        // 初始化影子状态值
        int X = 0;
        // 构造dp数组
        for (int i = 1; i < dp.length; i++) {
            for (int j = 0; j < 256; j++) {
                // 大部分回退情况都会委托影子状态处理
                dp[i][j] = dp[X][j];
            }
            dp[i][pattern.charAt(i)] = i + 1;
            // 更新影子状态
            X = dp[X][pattern.charAt(i)];
        }
    }
    public int search(String haystack, int[][] dp) {
        int N = haystack.length();
        int M = dp.length;
        int j = 0;
        for (int i = 0; i < N; i++) { // 遍历haystack字符串
            j = dp[j][haystack.charAt(i)];
            if(j == M) return i - M + 1;
        }
        return -1;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值