KMP

KMP算法:判断两个字符串的包含关系

一、知识储备

子串/子数组 : 元素之间必须相邻且连续
子序列: 元素之间相对前后顺序不变,无须相邻,
两子串的包含问题:
str1是否包含str2 若包含,则从str1的哪个字符开始
str1 abc123def
str2 123def

kmp相关概念

前缀子串: 从字符串首位到该字符的前一位置(不包括该位置)
后缀子串 : 从字符串第二位到该字符的前一位置
字符串中任意字符的匹配程度:该字符前缀子串与后缀子串相等的最大长度
字符串abcabcd
d的最长前缀子串为abcab
d的最长后缀子串为bcabc
next[] 表示每个位置的最长前缀和最长后缀的匹配程度
例子:

字符串 a b a b c d
next数组 -1 0 0 1 2 3

二、暴力方法

遍历str1每个位置,以该位置为起始的子串与str2匹配,时间复杂度O(m*n) 其中m>=n

三、KMP加速匹配过程

3.1流程图

  1. str1[i,X-1]与str2[0,Y-1]相等,str1[X]!=str2[Y]

KMP_第1张图片
2. 根据str2[Y]的next值,判断是否小于0
若小于0,说明Y是str2的首字符,与str1[X]不等,则j==X+1,str1从j位置与str2重新匹配
否则:找到str1中新的起始字符,记为j,此时判断str2[Z]==str1[X]
若相等,则继续X++,Z++,继续比较
若不等,则把Z当做Y,重复步骤2

KMP_第2张图片

例子

abcabct
abcabca
KMP_第3张图片

3.2kmp实质: 确定[i,j)位置上没有匹配str2的字符,可以通过反证法证明,实现了一次比较,跨越了str1上的k个字符

KMP_第4张图片

3.3求next数组

数学归纳法,有点类似DP
如下图,
步骤一:想求字符a的前缀后缀匹配最大值,只需知道其前一字符b的next值,
若next值<=0,则next[aIndex]=0
否则比较str2[x] == b
若相等,则next[aIndex] = next[bIndex]+1
KMP_第5张图片
若不相等,则把str2[c]当成步骤一中的b,重复这一过程,

KMP_第6张图片

KMP_第7张图片

/**
 * Created by huajianJin on 2019/10/11.
 */
public class KMP {

    static int[] getNextArray(String s) {
        if(s.length()< 0) {
            return new int[] {-1};
        }
        char[] chs = s.toCharArray();
        int[] next = new int[s.length()];
        next[0] = -1;
        next[1] = 0;
        int pos = 2;
        int lastCharNext = 0; // cn = next[pos-1]
        while(pos < next.length) {
            if(chs[pos - 1] == chs[lastCharNext]) {
                next[pos++] = ++lastCharNext; // cn = next[pos-1]
            } else if(lastCharNext > 0){
                lastCharNext = next[lastCharNext];
            } else { //  chs[0] != chs[pos-1] && lastCharNext <= 0 
                next[pos++] = 0;
            }
        }
        return next;
    }
    static int getIndex(String s1,String s2) {
        char[] chs1 = s1.toCharArray();
        char[] chs2 = s2.toCharArray();
        int r1 = 0;
        int r2 = 0;
        int next[] = getNextArray(s2);
        while(r1<s1.length() && r2<s2.length()) {
            if(chs1[r1]==chs2[r2]) {
                r1++;
                r2++;
            } else {
                if(next[r2] > 0) {
                    r2 = next[r2];
                } else { // r2 = 0,
                    r1++;
                }
            }
        }
        return r2 == chs2.length ? r1-r2 : -1;
    }

    public static void main(String[] args) {
        String s1 = "abcafbca";
        String s2 = "fb";
        int res = getIndex(s1,s2);
        System.out.println(res);
    }
}

四、总结

获得next数组的时间复杂度O(M),整体时间复杂度O(N)

你可能感兴趣的:(算法)