算法小知识-------02·22-------KMP

79 阅读3分钟

这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战

KMP算法

关于KMP算法,一般适用于字符串的部分匹配。

也就是我们的String的API —— indexOf()方法,但是区别在于一个是调库,一个是一种匹配的思路

理解过KMP算法后,意义在于脑海内会存在一个O(m+n)的API方便自己调用

带着实践去理解,会更容易思考。

例:

String a = "abcabcabf",String b = “abcabf”,求出字符串b出现在字符串a的首个下标

8.1 理解过程

首先,KMP需要分为2步,预处理前后缀 + 匹配字符串。

  • 所谓的前后缀,我们把字符串 b 拆分成前缀集合和后缀集合,分别是{a,ab,abc,abca,abcab},和{bcabf,cabf,abf,bf,f},下面为部分匹配表的数组

    charabababca
    index01234567
    PMT00123401

    这也就是我们需要的预处理next数组,意义在于,每次匹配到不相符时,可以从上一个公共前后缀再出发,而不需要在从头来过

  • 匹配字符串,就是从不匹配的部分,让其从数组中找出次大的下标重新出发

实现strStr()

该题出自力扣的28题 —— 实现strStr()【中等题】
这题也是典型的KMP练手题,没有任何胡里花俏

审题

实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回  -1 。

说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符

  • 首先定义相应的字符长度 + 字符串转换字符数组
  • 在字符串前 添加一个空格 作为哨兵下标,可以在后续遍历时不用考虑-1的情况
  • 定义next数组
    • 也就是上述所说的,找到最大前后缀交集,并把长度存于数组
    • 也是使用双指针,如果不匹配则为初始值 0 ;
  • 匹配字符串
    • 本意在于,主字符串(haystack)不需要从头再来,只需要一次遍历就可以
    • 如果匹配不成功,则下标回到当前的前后缀最大交集,也就是KMP的本意——不需要回到开头,省去一定成功的遍历
  • 当然了,Java的String方法也是可以使用indexOf()方法实现的。

编码

    public int strStr(String haystack, String needle) {
        if(needle.isEmpty()) return 0;
        int m = haystack.length(),n = needle.length();
        haystack = " " + haystack;
        needle = " " + needle;
        char[] ss = haystack.toCharArray();
        char[] pp = needle.toCharArray();

        int[] next = new int[n+1];
        //构建next数组,也就是 PTM
        for(int i=0,j =2;j<=n;j++){
            while(i>0 && pp[i+1] != pp[j])i = next[i];;
            if(pp[i+1] == pp[j])i++;
            next[j] = i;
        }

        for(int i=1,j = 0;i<= m;i++){
            while(j >0 && ss[i] != pp[j+1])j = next[j];
            if(ss[i] == pp[j+1])j++;
            if(j == n)return i - n;
        }
        return -1;

    }

image.png