字符串匹配之kmp

转自:http://hi.baidu.com/sector/blog/item/439c14dbe69e3c3932fa1c22.html

kmp主要就是计算前缀函数e[q]=max{k,k<q且Pk < Pq},表示前缀Pq的真后缀的最长前缀的长度.

int prefixComp(char p[],int e[])
{
    int m = strlen(p);
    int k = 0;
    e[1] = 0;
    for(int i=2;i<=m;++i)
    {
        while(k>0&&p[(k+1)-1]!=p[i-1]) k = e[k];
        if(p[(k+1)-1]==p[i-1]) ++k;
        e[i] = k;
    }
    return m;
}
void kmp(char s[],char p[])
{
    static int e[M] = {0};
    int n = strlen(s);
    int m = prefixComp(p,e);
    int k = 0;
    for(int i=1;i<=n;++i)
    {
        while(k>0&&p[(k+1)-1]!=s[i-1]) k = e[k];
        if(p[(k+1)-1]==s[i-1]) ++k;
        if( k == m )
        {
           printf("%d ",i-k);
           k = e[k];
        }
    }
    putchar('\n');
}

习题:试说明如何通过检查字符串PT的前缀函数e,来确定模式P在文本T中出现的位置.

解答:根据<的传递性,..< P(e[e[i]]) < P(e[i]) < Pi,即若x是P(e[e[e[..e[i]]]])的后缀,那么它是P(e[e[e[i]]])的后缀,那么也是P(e[e[i]])的后缀,..,是Pi的后缀.

int m = strlen(p);
strcat(p,s);
int e[M] = {0};
prefixComp(p,e);
int len = strlen(p);
for(int i=2*m;i<=len;++i)
{
    int k = e[i];
     while(k>m) k=e[k];
     if( k == m ) printf("%d ",i-2*m);
}

习题:写出一个线性算法,以确定文本T是否是另一个字符串T'的循环转移.例如,arc和car是彼此的循环转移.

解答:生成文本T'TT,然后计算前缀函数,扫描文本T'TT,若e[i]>=|T'|即可.

习题:设y^i表示字符串y与其自身并置i次所得结果.例如(ab)^3=ababab.如果对某个字符串y∈S和某个r>0有x=y^r,则称字符串x∈S具有重复因子r.设w(x)表示满足x具有重复因子r的最大值r.写出一有效算法计算出w(Pi)(i=1,2,...,m),算法的输入为模式P[1,..,m],算法运行时间是多少?

解答: 先证明:Pq是基于重复因子的字符串当且仅当Pk是基于重复因子的字符串且Pk满足e[q]=max{k:Pk>Pq},q-k|k.

==>: 因为Pq是基于重复因子r的字符串,所以Pq可以表示成yyy..yy=y^r的形式,显然,Pk=y^(r-1),且q-k=r-(r-1)=1|r-1.

<==:已知Pk是基于重复因子的字符串,且Pk满足e[q]=max{k:Pk&lt;Pq},q-k|k.因为Pq-k是Pk的前缀,Pq-k必然是Pk的因子,若Pq-k不是Pk的因子,那么存在Pq-k-k'是Pk的因子(k'<q-k),这样,Pk可以由Pq-k-k'表示成yyy..的形式,即q-k-k'|k,k'>0,这与q-k|k矛盾.因此,Pq是基于重复因子的字符串.

根据上面的命题,我们可以先计算出前缀函数e.然后对于Pi(i=1,2..,m),根据i-e[i]|e[i]来计算w.

算法如下:

int n = strlen(s);
int k = 0;
e[1] = 0;
for(int i=2;i<=n;++i)
{
      while(k>0&&s[k]!=s[i-1]) k=e[k];
      if(s[k]==s[i-1]) ++k;
     e[i] = k;
}
for(int i=1;i<=n;++i)
{
      if(i-e[i]!=0&&e[i]%(i-e[i])==0)
      {
           w[i] = i/(i-e[i]);
      }else
      {
          w[i] = 1;
      }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值