KMP 算法

字符串匹配算法,一种是朴素算法,即字符串S与匹配字符串P从0开始逐个匹配,失配后右移一位从新匹配。

另一种更高效的算法即是KMP算法。

例如:S[0,n],P[0 ,m]

如果S[ i ] != P[ j ]; 此时必有 式A:S[ i-j, i-1 ] = P[ 0 , j-1 ]  , 如果使用朴素算法则从S[ i-j+1] 开始与 P重新逐个匹配(即比较S[ i-j+1 ,i-j+1+m] 、P[0, m ]),这样时间复杂度为O(n*m),效率低。

若我们保持S索引不变(即先考虑索引 i 之前的部分)上述比较等价于 式B:S[ i-j+1 , i-1] =? P[ 0, j-2 ];式B对于式A来看,假如我们将S看成不动,则P相对S右移了一位。

在此假设 P右移K位,

1)、有 式C S[ i-j+k , i-1] = P[ 0 , j-k-1] ( 0<k<=j-1);同时由式A 得 式D S[ i-j+k , i-1] = P[  k, j-1 ]   (k < =j-1), A中 j-1位相等,后k位也相等;

    由 C 、D 可得  E:P[0 , j-k-1] = P[ k , j-1] ;故可得 P[ 0 , j-1] 子串的前缀=后缀。(前面j-k 个等于 后面 j-k个)

    令 next = j-k;由上述可得其只与P[ 0 ,j-i] 有关,而与S无关;

    故假如我们求出各P[ 0, j ] ( 0<=j <=m)对应的 next值,即我们需要得到一个Next[m+1] 数组保存对应的next值;则当在任意位置失配时我们可以得到对应的右移值 k = j-next;

2)、否则比较 S[ i ] 、P[ 0 ];

 

Next数组的求法:

假设 next[ j ] = t         ==>        P[ 0 , t-1]= P[ j-t+1 ,j]     ------  F;

如果 P[ t ] = P[ j+1 ]   ==>         next[ j+1] = t +1 = next[ j ] +1 -------G;

如果 P[ t ] != P[ j+1 ]  ,此时不能通过 next[ j ] 推导 next[ j+1 ],需要另行计算;

//假如P[ 0 , v-1] = P[ j-v+1 , j ] ( v < t)------------I;

//由 I、F得 P[ 0 , v-1] = P[ t-v , t-1] ( 因为 I中的两项分别为F中两项的子集) == 》 next[t-1] = v ---------------J

//若P[ v ]  = P[ j+1] 则 next[ j+1 ] = v + 1 = next[t-1] +1  ,否则继续循环。

换一种说法:

因next[ t ] = v (t <j , 故已经求得 ),则 P[ 0 , v-1] = P[ t-v+1 , t] = P[ j-v+1 , j]

如果 P[ v ]  = P[ j+1] 则 next[ j+1 ] = v + 1 否则继续循环

next 实例算法:

public static int[] getNext(char[] p)
    {
        if(p == null || p.length == 0)return null;
        int len = p.length;
        
        int i = 0 , j=-1;
        int[] next = new int[len];
        next[i] = -1;
        
        while(i<len-1)
        {
            if(j== -1 || p[i] == p[j])
            {
                ++i;
                ++j;
                
                if(p[i] != p[j])
                {//因为p[i] != p[next[i]](如果相等,移动后p[next[i]] == p[i] == s[index] 和原来一样失配)
                    
                    next[i] = j;
                    
                }
                else
                {//猜测如果
                    
                    next[i] = next[j];
                    
                }
            }
            else
            {//如果不相等,需要在前j个字符中获取最长的前缀=后缀的子串
                
                j = next[j];//新的j值,表示p[0,j) ==p[i-j,i)
                
            }
            
            SortHelper.showArray(next);
        }        
        return next;
    }

kmp 算法

public static int kmpSearch(char[] s , char[] p)
    {
        if(s== null || p== null)
        {
            return -1;
        }
        int sLen = s.length;
        int pLen = p.length;
        
        if(sLen <pLen)
        {
            return -1;
        }
        
        int[] next = getNext(p);
        
        int j=0;
        for(int i=0 ; i<sLen ;)
        {
            if(s[i] == p[j])
            {
                if(j== pLen -1)
                {
                    return i-j;
                }
                
                i++;
                j++;
            }
            else
            {
                j = next[j];
                if(j==-1)
                {
                    i++;
                    j++;
                }
            }            
        }
        
        return -1;
        
    }

参考链接:

http://blog.csdn.net/v_july_v/article/details/7041827

转载于:https://www.cnblogs.com/lipeil/archive/2012/11/30/2795686.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值