KMP算法---关于next数组最详细的解答

        一切都是最好的安排,但是我们为了实现最好的安排,每一步都要拼尽全力。

我们在一个母字符串中查找一个子字符串有很多方法。KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度。

kmp算法的精髓就在于next数组,从而达到跳跃式匹配的高效模式。




而next数组的值是代表着字符串的前缀与后缀相同的最大长度。

"前缀"指除了最后一个字符以外,一个字符串的全部头部组合;

"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

在KMP算法中有个数组,叫做前缀数组,也有的叫next数组,每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符,当然它描述的也是子串的对称程度,程度越高,值越大,当然之前可能出现再匹配的机会就更大。

这个next数组的求法是KMP算法的关键,但不是很好理解,我在这里用通俗的话解释一下。

1.举一个Next数组的例子,注意next()方法里面求的就是这个数组。 

下面来说明这个数组的求法:

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

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

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

重点:

         1.next数组的每个值的含义:在某个数组下标处,以数组下标处的字母结尾的字符串,前缀与后缀的最大重合长度。比如next[13]=7,就是在agctagcagctagc这个串中最大重合长度为7。     

         2. 如果想要计算next[14]的值,此时就需要寻求next[13]的帮助(知道前一位的最大重合长度,在前缀最大重合长度的基础上再多取一个字母即多取一个下标为7的字母,在后缀最大重合长度的基础上再多取一个字母即多取下标为14的字母,比较这两个字母是否相等,如果相等,则在next[13]的基础上+1,如果不相等见4)。

        3.但是因为数组是从0开始的,取这个最大重合长度,自然就定位到最大重合的下一个字符,所以直接取next[13]=7的7这个值,去找s[7],此时实际是第8个字符。

        4.(难点**) 如果不相等时也不直接去第一个字符处比较(确实不相等s[14]!=s[7]),相当于重复了在2的操作,在s[7]处,将a换成t(将s[7]换成了s[14])继续找s[7]处的最大重合长度(此时需要寻找next[6]的帮助),不断循环,直到无法找到重合子串,即到第一位与首字母a进行比较。

void makeNext(char s[],int next[])
{
    int len = strlen(s);
    next[0]=0;                    //初始化
    for(int i=1,k=0;i0 && s[k]!=s[i]) //s[k]!=s[i]即完成s[i]和s[next[i-1]]的比较,
                                 //不相等,则需要进入循环不断往前追溯。即步骤4
            k=next[k-1]; 
            //等价于  k=next[next[i-1]-1]
            //等号右边的k起的只是下标的作用
        if(s[k]==s[i])            
            k++;                  //相等就+1
        next[i]=k;                //赋值
    }
}
 

至此,KMP算法的Next数组求完。

KMP全部代码如下

public class UseString {


    //有了Next数组,现在来写字符串匹配算法吧。
    public int KMP(String s1,String s2){
        //求的模式串的Next数组
        int i=0,j=0;
        int[] next=next(s2);
        for (int s:next
        ) {
            System.out.println(s);
        }
        //Next数组输出完毕
        for (;i0&&s1.charAt(i)!=s2.charAt(j)){
                j=next[j-1];
            }
            if (s1.charAt(i)==s2.charAt(j)){
                j++;
            }

            if (j==s2.length()){
                return i-j+1;
            }
        }
        return -1;
    }

    public int[] next(String s){
        //获取字符数组
        char[] chars=new char[s.length()];
        for (int i=0;i0&&chars[i]!=chars[k]){
                k=next[k-1];
            }
            if (chars[i]==chars[k]){
                k++;
            }
            next[i]=k;

            //判断循环结束的条件:为什么要这个循环呢?就是往next里面跳,不断的匹配最短的跳
            // 1.k值等于0(到了最前面)
            // 2.这个不匹配,要往前跳

        }
        return next;

    }



    public static void main(String[] args) {
        String test1="adaagctagcagctagctg";
        String test2="agctagcagctagctg";
        UseString useString=new UseString();

        System.out.println(useString.KMP(test1, test2));


    }
}

你可能感兴趣的:(java,算法,开发语言,数据结构)