【左神算法】 KMP算法

1.思路

字符串匹配,通常我们使用的是互相比较,也就是 str1 : ABCABCD st2 : ABCABCT
str1 和 str2按照顺序比较。当匹配到str1的D 和 str2的T位置可以发现,不等。则直接从str1的第一个A+1位置在匹配。可以发现这样就是穷举的方法,相对来说是一种时间复杂度比较高的。因此,就出现了KMP算法。

KMP字符串匹配算法: KMP的核心思想是先计算出当前字符的最长前缀和最长后缀的最长长度。不包含第一个前缀和最后一个后缀。
举一个栗子,ABCABCD D的最长子串是多少 为ABC ABC 为3,而AAAAD D的最长子串长度为3 不能包含第一个A和最后一个A。而在计算KMP算法之前需要先计算出最长子串的长度。ababac 的最长子串的长度分别为 [-1,0,0,1,2,3]。
基于最长子串长度这个数组,就可以实现KMP算法。还是计算str1: abkababkabd str2:abkababkabf 当str1移动到d位置 str2移动到f位置,发现不相等。而我们之前的做法是将str1的指针移动到开头a+1的位置,也就是b的位置从先计算。但是KMP算法基于之前计算的最长子串数组,可以有一个很大的改进,那就是不需要从a+1的位置开始。
ps:重点来了。会取出str2中f的前一个位置最长子串数组的长度。为5(最长串为 abkab)从str1的a+5位置比较。这样就避免了a+1 一个位置一个位置的重复比较。为什么这样比较呢。因为在str1中的第一个最长子串abkab已经比较过了。不存在相同的,所以直接从a+5的位置比较。
那么在str1 a到a+5位置开始的位置到最终str1末尾的位置会不会存在一个和str2想匹配的字符串呢。
我们假设存在一个上述的条件,在已知str2最长子串中,如果存在一个那么势必就是str1的最长后缀子串,所以第一个条件是不存在的。
好了,上面就是kmp算法的核心。
接下来,我们来梳理一下KMP算法的流程。
getNextArray();首先需要初始化0 1 位置的参数,pos的表示当前位置大小。而cn表示当前位置的之前的最长子串的长度为多少。如果当前位置的前一个位置str2[pos-1]和str2[cn] 也就是最大子串的长度的位置元素相等。当前pos的cn++.

2.code

package com.ncst.improve.one;

/**
 * @author i
 * @create 2020/6/30 17:04
 * @Description
 */
public class KMP {

    public static int kmp(String str1,String str2){
        if (str1 == null || str2 == null || str1.length()<str2.length() || str2.length() < 1){
            return -1;
        }
        char [] ch1 = str1.toCharArray();
        char [] ch2 = str2.toCharArray();
        int c1 = 0;
        int c2 = 0;
        int [] next = getNextArray(ch2);
        while (c1<ch1.length && c2<ch2.length){
            if (ch1[c1] == ch2[c2]){
                c1++;
                c2++;
            }else if (next[c2] == -1){
                c1++;
            }else {
                c2 = next[c2];
            }
        }
        return c2 == ch2.length ? c1-c2 : -1;
    }

    public static int [] getNextArray(char [] str2){
        if (str2.length == 1){
            return new int [-1];
        }
        int [] next = new int [str2.length];
        next[0] = -1;
        next[1] = 0;
        int pos = 2;//走到那个位置
        int cn = 0;//当前位置的最长子串个数
        while (pos<next.length){
            if (str2[pos-1] == str2[cn]){
                next[pos++] = ++cn;
            }else if (cn>0){
                cn = next[cn];
            }else {
                next[pos++] = 0;
            }
        }
        return next;
    }

    public static void main(String[] args) {
        String str = "abdabc";
        String str2 = "abc";
        System.out.println(kmp(str, str2));
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章