KMP算法

我们首先用一个图来描述kmp算法的思想。在字符串O中寻找f,当匹配到位置i时两个字符串不相等,这时我们需要将字符串f向前移动。常规方法是每次向前移动一位,但是它没有考虑前i-1位已经比较过这个事实,所以效率不高。事实上,如果我们提前计算某些信息,就有可能一次前移多位。假设我们根据已经获得的信息知道可以前移k位,我们分析移位前后的f有什么特点。我们可以得到如下的结论:
A段字符串是f的一个前缀。
B段字符串是f的一个后缀。
A段字符串和B段字符串相等。

如果比到第i的位置如果不相同了,字符串f可以向前移动几位呢?如果前i-1位中前缀和后缀的最大公共长度next[i-1],那么就可以移动i-1-next[i-1]了,因此要求数组next。

理解了kmp算法的基本原理,下一步就是要获得字符串f每一个位置的最大公共长度。这个最大公共长度在算法导论里面被记为next数组。在这里要注意一点,next数组表示的是长度,下标从1开始;但是在遍历原字符串时,下标还是从0开始。假设我们现在已经求得next[1]、next[2]、……next[i],分别表示长度为1到i的字符串的前缀和后缀最大公共长度,现在要求next[i+1]。由上图我们可以看到,如果位置i和位置next[i]处的两个字符相同(下标从零开始),则next[i+1]等于next[i]加1。如果两个位置的字符不相同,我们可以将长度为next[i]的字符串继续分割,获得其最大公共长度next[next[i]],然后再和位置i的字符比较。这是因为长度为next[i]前缀和后缀都可以分割成上部的构造,如果位置next[next[i]]和位置i的字符相同,则next[i+1]就等于next[next[i]]加1。如果不相等,就可以继续分割长度为next[next[i]]的字符串,直到字符串长度为0为止。代码如下:
public int[] getNext(String b)
{
    int len=b.length();
    int j=0;
       
    int next[]=new int[len+1];//next表示长度为i的字符串前缀和后缀的最长公共部分,从1开始
    next[0]=next[1]=0;
       
    for(int i=1;i<len;i++)//i表示字符串的下标,从0开始
    {//j在每次循环开始都表示next[i]的值,同时也表示需要比较的下一个位置
        while(j>0&&b.charAt(i)!=b.charAt(j))j=next[j];
        if(b.charAt(i)==b.charAt(j))j++;
        next[i+1]=j;
    }
       
    return next;
}
KMP算法的代码如下:

public void search(String original, String find, int next[]) {
    int j = 0;
    for (int i = 0; i < original.length(); i++) {
        while (j > 0 && original.charAt(i) != find.charAt(j))
            j = next[j];
        if (original.charAt(i) == find.charAt(j))
            j++;
        if (j == find.length()) {
            System.out.println("find at position " + (i - j));
            System.out.println(original.subSequence(i - j + 1, i + 1));
            j = next[j];
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值