KMP 算法

介绍

1、解决模式串在文本串是否出现过,如果出现过,最早出现的位置的经典算法

2、利用之前判断过信息,通过一个 next 数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过 next 数组找到,前面匹配过的位置

3、文本串长度为 n,模式串长度为 m,时间复杂度 O(n + m)

4、很详尽KMP算法

5、代码随想录: KMP 详解

 

概念

1、前缀:包含首位字符但是不包含末位字符的子串

2、后缀:包含末位字符但是不包含首位字符的子串

3、next[i] = j:即模式串 P 的前i + 1个字符(包括i)组成的字符串中,最长公共前后缀子串的长度为 j

 

步骤

1、通过自匹配获取模式串 P 的 next[],该 next[] 实际上是前缀表:i 对应值表示,下标 i 之前(包括 i)的字符串中,最长公共前后缀的长度

(1)初始化:i 代表后缀末尾,j 代表前缀末尾,i 初始为1,j 初始为 0,next[0] = 0

(2)前后缀不同: 保证 j > 0 情况下,令 j = next[j - 1];

(3)前后缀相同:i++,j++

(4)更新 next 数组:next[i] = j;

(5)模式串遍历完毕

2、 i 指针初始指向文本串 S 下标 0,j 指针初始指向模式串 P 下标 0,字符匹配时,i++,j++

3、当文本串 S 与模式串 P 下标为 a 的字符不匹配时

(1)根据前一个字符下标 j - 1,获取 next[j - 1] == c,即模式串 P 前 j 个字符(不包括 j)组成的子串中,最长公共前后缀字串长度为 c

(2)模式串 P 的指针 j 应该回退到下标 c,j = c,转步骤 2

4、直到遍历完文本串,返回 -1;或模式串完全匹配,返回 i - j + 1

 

代码实现

public class KMP {

    //KMP算法
    public static int kmpMatch(String source, String target) {
        int[] next = getNext(target);
        for (int i = 0, j = 0; i < source.length(); i++) {
            while (j > 0 && source.charAt(i) != target.charAt(j)) {
                j = next[j - 1];
            }
            if (source.charAt(i) == target.charAt(j)) {
                j++;
            }
            if (j == target.length()) {
                return i - j + 1;//匹配成功,+1原因:最后字符相同时,有j++,要+1
            }
        }
        return -1;
    }

    //获取一个target的next数组
    public static int[] getNext(String target) {
        int[] next = new int[target.length()];//创建next数组
        next[0] = 0;//下标0没有前缀、后缀,最长公共前后缀子串的长度为0
        for (int i = 1, j = 0; i < target.length(); i++) {
            while (j > 0 && target.charAt(i) != target.charAt(j)) {//前后缀不同,令j回溯,使前后缀相同或j为0
                j = next[j - 1];//根据j前一个元素值,就是j要回溯到的下标
            }
            if (target.charAt(i) == target.charAt(j)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}

 

 

next[] 优化

1、假设优化前 next[] 为:最长公共前后缀子串长度表,后移一位,next[0] = -1(与上文实现无关),当字符不匹配时,模式串 P 的指针 j 回退到下标 next[j] == c

2、问题:当字符不匹配时,模式串 P 的指针 j 回退到下标 next[j] == c,若模式串 P 下标 j 和下标 c 的元素相同,字符再次匹配时必定失败

3、不能允许 P[j] == P[c],两值相等,递归,即令 next[j] = next[c],直到两值不相等

 

代码实现

public class KMP {

    //KMP算法
    public static int kmpMatch(String source, String target) {
        int[] next = getNext(target);
        int i = 0;//指向文本串
        int j = 0;//指向模式串
        while (i < source.length() && j < target.length()) {
            if (j == -1 || source.charAt(i) == target.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if (j == target.length()) {
            return i - j;
        } else {
            return -1;
        }
    }

    //获取一个target的next数组
    public static int[] getNext(String target) {
        int[] next = new int[target.length()];//创建next数组
        next[0] = -1;
        int i = 0;
        int j = -1;
        while (i < target.length() - 1) {
            if (j == -1 || target.charAt(i) == target.charAt(j)) {
                i++;
                j++;
                if (target.charAt(i) != target.charAt(j)) {
                    next[i] = j;
                } else {
                    next[i] = next[j];
                }
            } else {
                j = next[j];
            }
        }
        return next;
    }
}

 

posted @   半条咸鱼  阅读(90)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示