KMP算法

    // TODO: 2018/4/9 比较重要的KMP算法 要理解 并且可以手写出来
    @Test
    public void testKMP(){
        char[] str = "bacbababadababacambabacaddababacasdsd".toCharArray();
        char[] ptr = "ababaca".toCharArray();
        int a = KMP(str, 36, ptr, 7);
        System.out.println(a);
    }

计算next数组:

   public void cal_next(char[] str, int[] next, int len)
    {
        next[0] = -1;//next[0]初始化为-1,-1表示不存在相同的最大前缀和最大后缀
        int k = -1;//k初始化为-1
        for (int q = 1; q <= len-1; q++)
        {
            while (k > -1 && str[k + 1] != str[q])//如果下一个不同,那么k就变成next[k],注意next[k]是小于k的,无论k取任何值。k标识的是已经有几个位置是一样的了
            {
                k = next[k];//往前回溯
            }
            if (str[k + 1] == str[q])//如果相同,k++
            {
                k = k + 1;
            }
            next[q] = k;//这个是把算的k的值(就是相同的最大前缀和最大后缀长)赋给next[q]
        }
    }

KMP算法:

 // KMP算法
    int KMP(char[] str, int slen, char[] ptr, int plen)
    {
    int[] next = new int[plen];
        cal_next(ptr, next, plen);//计算next数组
        int k = -1;
        for (int i = 0; i < slen; i++)
        {
            while (k >-1&& ptr[k + 1] != str[i])//ptr和str不匹配,且k>-1(表示ptr和str有部分匹配)
                k = next[k];//往前回溯
            if (ptr[k + 1] == str[i])
                k = k + 1;
            if (k == plen-1)//说明k移动到ptr的最末端
            {
                //cout << "在位置" << i-plen+1<< endl;
                //k = -1;//重新初始化,寻找下一个
                //i = i - plen + 1;//i定位到该位置,外层for循环i++可以继续找下一个(这里默认存在两个匹配字符串可以部分重叠),感谢评论中同学指出错误。
                return i-plen+1;//返回相应的位置
            }
        }
        return -1;
    }

String str = "bacbababadababacambabacaddababacasdsd";// 长度为n

String ptr = "ababaca";// 长度为m

最笨的办法是拿着长度为m的ptr子串到长度为n的str串中逐一去匹配,每次匹配的时候str的步数是1,ptr的步数也是1.所以时间复杂度就是O((n-m)*m);

KMP算法是充分利用了目标字符串ptr的性质,比如里面部分字符串的重复性,即使不存在重复字段,在比较时,实现最大的移动量,

每次匹配的时候str的步数是1,ptr的步数用可能的最大的移动量,也就是next数组的值。

 

第一步:计算next数组

概念:相同的最长前缀和最长后缀的长度

这里的前缀,后缀的概念:

String ptr = "ababaca"的最长前缀是“ababac”,

前缀:不能包括最后一个字符,

后缀概念也相同,不能包括第一个字符 。

 

对于目标字符串:"ababaca" 有如下

 next数组就是最大的移动量,在计算next数组的时候,

int k,就是累计已经匹配相同的前缀后缀子串前缀的下标,到下一个元素的时候,看这个元素是否相等,

若相等,k再次累加1,如果不相等,k=next[k],犹豫k是目前已经累积相等的子串的长度的下标,所以next[k]一定小于k

 第二步:利用next数组的计算结果来匹配

在这一步里 k标记的是ptr串中累计的已经匹配的长度的下标值。待k累计到ptr的长度-1 m-1的时候,就表示完整匹配到了目标子串。

posted @ 2018-04-10 14:13  乔胖胖  阅读(249)  评论(0编辑  收藏  举报