无双客栈

symbian linux e17 vim

KMP算法的改进。

KMP算法对朴素算法的改进,在于利用每次已得到的部分匹配:s[k+1..i]=t[1..j],但s[i+1]≠t[j+1],j<m。它隐含着主串s中从l+1开始的子串不可能与t[1..m]匹配完全,其中l=k,k+1,…,k+j-π[j]-1(即i-π[j]-1)。因而完全匹配的测试可大步地往前推进到s中从i+1-π[j]开始的子串,而且对于它,我们已有部分匹配s[i+1-π[j]..i]= t[1..π[j]]。如果π[j]>0,为扩展这部分匹配,接着需要测试的是s[i+1]=t[π[j]+1]?但已知s[i+1]≠t[j+1],所以,如果有t[π[j]+1] =t[j+1],那么可以肯定si+1-π[j]开始的子串,不可能与t[1..m]完全匹配。因而完全匹配的测试可再次推进到s中从i+1-π[π[j]]开始的子串,且我们再次有与t[1..m]的部分匹配s[i+1-π[π[j]]..i]=t[1..π[π[j]]],如果π[π[j]]>0。接着为扩大该部分匹配,需要测试的是s[i+1]= t[π[π[j]]+1],如果又有t[π[π[j]]+1]=t[j+1],则完全匹配的测试还可以继续往前推进,直到t[π[π[…[π[j]]…]]+1]≠t[j+1]。

这表明对于已得到的部分匹配:

s[k+1..i]=t[1..j],s[i+1]≠t[j+1],j<m …………………………………(3.3.1)

如果    t[j+1]= t [π[j]+1]=…=t[π[π[…[π[j]]…]]+1]……………………(3.3.2)

                             e

       π[π[…[π[j]]…]]=0

       π[π[…[π[j]]…]]≠0但t[j+1]≠t[π[π[π[…[π[j]]…]]]+1]…(3.3.3)

                                            e+1

那么,相应的完全匹配的测试可以直接推进到s中从i+2或i+1-π[π[π[…[π[j]]…]]]

                                                          e+1

开始的子串,且对后一种情况,该子串与t[1..m]已有部分匹配:

s[i+1-π[π[π[…[π[j]]…]]]..i] =t[1..π[π[π[…[π[j]]…]]]]

e+1                                  e+1

,而接着为扩大部分匹配需要做的测试是比较

s[i+1]与t[1]或s[i+1]与t[π[π[π[…[π[j]]…]]]+1]。

e+1

换句话说,在满足(3.3.1)、(3.3.2)、(3.3.3)且e≥1的情况下,完全匹配的测试允许比KMP算法有更大步的推进,即KMP算法可以作进一步的改进。

从上面的分析,我们不仅看到KMP算法可以进一步改进,而且可看出相应的改进措施:在原前缀函数π[q]的基础上,引入修改的前缀函数π[q],q=1,2,3,…,m.

       π[π[q]]     当π[q]≠0且t[q+1]=t[π[q]+1]

π[q]=   

               π[q]        当π[q]=0或t[q+1]≠t[π[q]+1]

修改后的前缀函数(Modified_Prefix_function)可实现如下:

Proc. Modified_Prefix_function(var  t:string;  var  ppi: array[1..t.length]  of integer);

var   m,q,k:integer;

pi:array[1..t.length]  of  integer;

begin   

compute-Prefix-function(t,pi);

m:=t.length;

for q:=1to m do

begin

k:=pi[q];

whilek>0 and t.chs [q+1]=t.chs[k+1] do k:=pi[k];

ppi[q]:=k

end

end;

KMP算法相应的改进只要将调用函数compute_Prefix_function改为调用函数Modified_Prefix_function,其他地方不作任何修改。

经过修改后的KMP算法,在最坏情况下的时间复杂性虽然没有明显改进,但对于那些使π[q]<π[q]的t[1‥m]将有明显改进,而且π[q]-π[q]越大改进越明显。例如s=aaaabaaaaab,t=aaaaab

 

 

1

2

3

4

5

6

 

 

π

0

1

2

3

4

0

 

 

π

0

0

0

0

0

0

 

 

 

完全匹配从对s的子串s[1..6]进行测试开始,在得到部分匹配s[1..4]=t[1..4],s[5]≠t[5],排除s[1..6]与t[1..6]完全匹配的可能之后,若按原KMP算法,要依次分别比较s[5]与t[4],s[5]与t[3],s[5]与t[2],和s[5]与t[1],依次排除s[2..7],s[3..8],s[4..9]和s[5..10]与t[1..6]完全匹配的可能,才能推进到测试s[6..11]与 t[1..6]的完全匹配而得到需要的结果。若按修改的KMP算法,由于4+1-π[4]=5,马上可排除s[2..7],…,s[4..9]与t[1..6]完全匹配的可能,将完全匹配的测试直接推进到s[5]开始的子串。发现s[5]≠t[1]后又排除s[5..10]与t[1..6]完全匹配的可能,从而推进到测试s[6..11]与t[1..6]的完全匹配,得到正确的结果。其中省却了s[5]与t[4],s[5]与t[3],s[5]与t[2]的比较。(注意s[5]与t[1]的比较尽管是多余的,但π[4]=0且s[5]=t[1],按照修改后的KMP算法,还得做s[5]与t[1]的多余比较。)

结论:对KMP的改进实际上是KMP对朴素算法改进的继续,即充分利用部分匹配的信息,不仅充分利用s[k+1..i]=t[1..j](挖掘可能隐含的匹配)而且充分利用s[i+1]≠t[j+1](挖掘可能隐含的不匹配)。

无双 注释:

优化点:

KMP算法是对朴素算法进行隐藏匹配的优化

KMP改进算法是对KMP算法隐含不匹配的优化


void Modified_Prefix_function(vector<int>& next,const char* t)
{
    //    char *x, int m, int kmpNext[]) {
    int    t_len    = strlen(t);
    next.swap(vector<int>(t_len+1,0));    //  调整next数组,因为next数组从第二个元素开始使用, 长度为strlen(t)+1

    int    j = next[0] = -1;
    for(int i =0;i < t_len;  ){
    //  与传统KMP算法不同就是: 如果下一个是=第一个, 那当前的值-1
    //  而匹配时, 无论哪一步都会++
    while ( j > -1 && t[i] != t[j] ){
        printf("j[%d] > -1 && t[%d]:%c != t[%d]:%c next[%d]:%d\n",j,i,t[i],j,t[j],j,next[j]);
        j    = next[j];
    }

    i++;
    j++;

    next[i] = t[i] == t[j]?next[j]:j;
    printf("next[%d]  = %c == %c ?next[%d]:%d:%d;\n",i,t[i],t[j],j,next[j],j);
    }
}

static void Modified_KMP_Matcher(const char* s,const char* t)
{
    vector<int>            next;
    Modified_Prefix_function(next,t);

    {
    printf("next:\n\t");
    for(int i =0;i<next.size();i++){
        printf("% 2d ",next[i]);
    }
    printf("\n");
    }
    int    s_len    = strlen(s);
    int t_len    = strlen(t);
    int    next_idx=0;
    for( int i =0; i< s_len; i++ ){
    while( next_idx>-1 && s[i] != t[next_idx] )
        next_idx    = next[next_idx];
    next_idx    ++;
    if( next_idx >= t_len ){
        printf("Mod_KMP_Matcher:S[%s]\tT[%s] pos[%d]\n",s,t,i - t_len+1);
        next_idx = next[next_idx];
    }
    }
}




Trackback: http://tb.donews.net/TrackBack.aspx?PostId=783999


[点击此处收藏本文]  发表于2006年03月23日 9:34 AM




正在读取评论……

发表评论

大名:
网址:
验证码
评论 
   

news

导航

blog stats

文章

收藏

相册

BBS

linux

存档


正在读取评论……