KMP 算法

基础原理参见:http://www.cnblogs.com/c-cloud/p/3224788.html

next 数组构造方法

(1)逐个查找对称串。

这个很简单,我们只要循环遍历这个子串,分别看前1个字符,前2个字符,3个... i个 最后到15个。

第1个a无对称,所以对称程度0

前两个ag无对称,所以也是0

依次类推前面0-4都一样是0

前5个agcta,可以看到这个串有一个a相等,所以对称程度为1前6个agctag,看得到ag和ag对成,对称程度为2

这里要注意了,想是这样想,编程怎么实现呢?

只要按照下面的规则:

a、当前面字符的前一个字符的对称程度为0的时候,只要将当前字符与子串第一个字符进行比较。这个很好理解啊,前面都是0,说明都不对称了,如果多加了一个字符,要对称的话最多是当前的和第一个对称。比如agcta这个里面t的是0,那么后面的a的对称程度只需要看它是不是等于第一个字符a了。

b、按照这个推理,我们就可以总结一个规律,不仅前面是0呀,如果前面一个字符的next值是1,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是1,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明对称程度就是2了。有两个字符对称了。比如上面agctag,倒数第二个a的next是1,说明它和第一个a对称了,接着我们就把最后一个g与第二个g比较,又相等,自然对称成都就累加了,就是2了。

c、按照上面的推理,如果一直相等,就一直累加,可以一直推啊,推到这里应该一点难度都没有吧,如果你觉得有难度说明我写的太失败了。

当然不可能会那么顺利让我们一直对称下去,如果遇到下一个不相等了,那么说明不能继承前面的对称性了,这种情况只能说明没有那么多对称了,但是不能说明一点对称性都没有,所以遇到这种情况就要重新来考虑,这个也是难点所在。

(2)回头来找对称性

这里已经不能继承前面了,但是还是找对称成都嘛,最愚蠢的做法大不了写一个子函数,查找这个字符串的最大对称程度,怎么写方法很多吧,比如查找出所有的当前字符串,然后向前走,看是否一直相等,最后走到子串开头,当然这个是最蠢的,我们一般看到的KMP都是优化过的,因为这个串是有规律的。

在这里依然用上面表中一段来举个例子:   

位置i=0到14如下,我加的括号只是用来说明问题:

(a g c t a g c )( a g c t a g c) t

我们可以看到这段,最后这个t之前的对称程度分别是:1,2,3,4,5,6,7,倒数第二个c往前看有7个字符对称,所以对称为7。但是到最后这个t就没有继承前面的对称程度next值,所以这个t的对称性就要重新来求。

这里首要要申明几个事实

1、t 如果要存在对称性,那么对称程度肯定比前面这个c 的对称程度小,所以要找个更小的对称,这个不用解释了吧,如果大那么t就继承前面的对称性了。

2、要找更小的对称,必然在对称内部还存在子对称,而且这个t必须紧接着在子对称之后。

如下图说明。

这里给出代码:

public int kmp(char[] t, char[] p)
    {
        int n = t.length;
        int m = p.length;
        int[] next = new int[m];
        makeNext(p,next); // 根据模式串计算 next数组即可
        // c: t中第c个字符, k代表已经匹配的长度
        for(int c = 0, k = 0; c < n ; ++ c ) // 模式串的第k位与原串第c位比较 
        {
            while(k > 0 && p[k] != t[c])
                k = next[k-1]; 
            
            if(p[k] == t[c]) ++ k;
            
            if(k == m) return c - m + 1;
        }
        return -1;
    }
    //根据  pattern 串来初始化 next 数组
    public void makeNext(char p[], int[] next)
    {    
        next[0] = 0; // pattern 串第一个字符最大前后缀长度为 0 
        int c = 1, k = 0; // c pattern 串下标, k 最大前后串
        for( ; c < p.length ; ++ c)
        {
            while(k > 0  && p[c] != p[k]) // 查找子对称
                k = next[k-1];
            
            if(p[c] == p[k]) ++ k; // 对称程度 + 1
            
            next[c] = k;
        }
    }
View Code

 

参考: http://blog.csdn.net/yearn520/article/details/6729426

posted @ 2015-04-18 18:05  ooon  阅读(420)  评论(0编辑  收藏  举报