今天我果然没爆零。(此处是一个滑稽)

为了鼓励自己,我们来看毛片呀

欢迎特邀嘉宾KMP(掌声又响起)

 

大家好,我是KMP。今天在爆零小飞侠的邀请下来和大家见面。

我可以找相同的字符串(数组)?

比如我能告诉你们ababaccccbaba的身体里有几个的aba

 
 哈?为什么要用我,还不是因为正常用暴力双重循环容易爆时间吗?(傲娇脸)【暴力O(mn),KMP O(m + n)】}
什么?为什么暴力会爆时间?哼,因为我帅呀。(开玩笑的)
正解:比如说在abababcdsa里找abc,用暴力找发现那个长字符串里前两位和短字符串里一样,但找到第三个却发现不一样了。用暴力就要把abc对照长字符串的第二位开始重新找,直到找到正确答案。
但是用我可以直接滑到字符串的第三位开始匹配,看上去就节省了很多时间对不对?
 
什么?这个操作我是怎么做到的?
好吧,我告诉你们。
这是从网上当下来的图
m和s字符串在正常的匹配,在 i 位置之前它们完美匹配,在 i 位置后它们就不匹配了。
为了尽快挪到下一个可能匹配的位置,要求出 i 位置之前的m字符串前缀和后缀的最长公共长度(前缀A后缀B)。
A = B
然后将m向右移动至A与B重合,这就是下一个可能出现匹配字符串的地方。
直到移动后的m和s再次出现不匹配时,重复此操作,直到匹配成功。
 
嗯,我听到爆零小飞侠同志问next数组是什么了。是不是在网上找各种各样关于我的介绍都能看到它?
那我就在此解释一下好了。这个next数组是指每次匹配不成功后长字符串向前的长度。
我们首先把next [ 0 ]赋初值为-1相同表示不存在前后缀公共长度。
然后把两个字符串一位一位比较是否相同,当比较到第k 位时,如果二者相同,就使next[ k ]的值加一;如果不相同,就把k的值回溯到next[ k ]。
 然后就没有然后了,next[ k ]的值就这么求出来了,你们还在期待些什么......
 
 
 
这还是从网上当下的图
具体来说就是
 
好了现在大家要见到我的真身了
public class KMP {

    public static void main(String[] args) {
        String s = "abacababc";
        String p = "abab";
        System.out.println(Index_KMP(s, p));
    }
    
    //优化过后的next数组求法 
    public static int[] next(String p) {
        int[] next = new int[p.length()];
        int k = -1, j = 0;
        next[0] = -1;        //    初值为-1    
        
        while(j < p.length() - 1) { 
            //    p[k]表示字符串的前缀,p[j]表示字符串的后缀
            if(k == -1 || p.charAt(k) == p.charAt(j)) {  // 判断的先后顺序不能调换
                k++;
                j++;
                //    后面即是求next[j+1]的过程
                if(p.charAt(k) == p.charAt(j))             //  此处等价于if(p[j] == p[ next[j] ])
                    //    因为不能出现p[j] = p[ next[j] ],所以当出现时需要继续递归,k = next[k] = next[next[k]]
                    next[j] = next[k];                    //  此处等价于next[j] = next[ next[j] ]
                else    
                    next[j] = k;
            }
            else {
                k = next[k];         
            }
        }
    
        return next;
    }
    
    public static int Index_KMP(String S, String P) {
        int i = 0, j = 0;
        int[] next = next(P);
        
        while(i < S.length() && j < P.length()) {      
            if(j == -1 || S.charAt(i) == P.charAt(j)) {        //    如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++. 注意:这里判断顺序不能调换! 
                i++;
                j++;
            }
            else
                //    如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]      
                //    next[j]即为j所对应的next值,效果为进行回溯        
                j = next[j];
        }
        
        if(j == P.length())
            return i - j;
        else 
            return -1;
    }
    
}

帅吧。

那个节目时间结束,我要走了,可千万别爱上我哟。

爆零小飞侠:您赶紧滚吧,我此生都不想在看到您了......

 

所以说这篇博客完结了,我还是不会写KMP的代码(此处有一个坚强无比的微笑)

不得不说这个KMP看的我一脸懵逼,同志们要是有哪个地方我写错了,一定要告诉我......

                                                                                                                ------来自一名学渣的呻吟