KMP算法中的循环节问题

循环节问题包括 完全循环 和 不完全循环

完全循环

       对于一个具有循环节并且长为n的字符串,其循环节长为 n - n x t [ n - 1 ] ,并且满足n % ( n - n x t [ n - 1 ] ) ==0 , 这里nxt [ ]是KMP算法中的next 数组,并且是对于本博客上上上篇给出的KMP模板的方法构造nxt数组来说的。

本篇博客KMP算法的讲解在这里:KMP板子

(不同的构造方法可能式子不一样,比如有的方法下可能是 n - n x t [ n ] )

不完全循环

      不完全循环是说需要补上一些位才能构成完整的循环,比如abca,就可以称为一个不完全循环,需要补上bc才能构成一个完整的循环。而完全循环则是类似abcabc这样的。设字符串长度为 len ,那循环节长度就是 len-nxt [len-1],需要补全的位数就是循环节长度-nxt [ len-1 ]%循环节长度。

证明

其实写出一个字符串模拟一下就能感觉到。

        比如给出字符串:abcdabcdabcd,长度为12,而我们 nxt [ 12-1 ] = nxt [ 11 ] = 8,根据这个nxt数组的定义,它表示的是这个字符串前缀和后缀相等的最大长度,如果是完全循环,那么剩下的也就是 len-nxt [ 11 ]当然就是最大的循环节长度了。

        如果把字符串改为:abcdefabc,我们可以知道,循环节算的方法跟完全循环的一样,就是len - nxt [ len-1 ]=L,即abcdef,那么 nxt [ len-1 ] 就是最后剩下的一串字符,它可能大于循环节也可能小于循环节(大于循环节的时候:len - nxt [ len-1 ] < nxt [ len-1 ],即 2 * nxt [ len-1 ] > len,比如azaza的情况)因此我们要用nxt [len-1] %循环节长度表示剩下的,而需要补全的当然就是循环节长度减去这个值啦。

 

下面是例题:

完全循环

HDU - 1358

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 1000005

using namespace std;
char s[maxn];
int nxt[maxn];
int n;

void build()//构造nxt表
{
     int k=0;
     nxt[0]=0;
     for(int i=1;i<n;i++)
     {
          while(k&&s[i]!=s[k])
               k=nxt[k-1];
          if(s[i]==s[k])
               k++;
          nxt[i]=k;
     }
}
int main(void)
{
     int kase=0;
     while(~scanf("%d",&n)&&n)
     {
          memset(nxt,0,sizeof(nxt));
          scanf("%s",s);
          build();
          printf("Test case #%d\n",++kase);
          for(int i=1;i<n;i++)
          {
               int len=i+1;
               int mid=len%(len-nxt[len-1]);//求出循环节长度
               if(mid==0&&nxt[len-1]!=0)
               {
                    int res=len/(len-nxt[len-1]);
                    printf("%d %d\n",len,res);
               }
          }
          printf("\n");
     }
     return 0;
}

不完全循环

HDU - 3746 

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100005

using namespace std;
char s[maxn];
int nxt[maxn];
int n;

void build()
{
     int k=0;
     nxt[0]=0;
     for(int i=1;i<n;i++)
     {
          while(k&&s[i]!=s[k])
               k=nxt[k-1];
          if(s[i]==s[k])
               k++;
          nxt[i]=k;
     }
}
int main(void)
{
     int t;
     int ans;
     scanf("%d",&t);
     while(t--)
     {
          scanf("%s",s);
          n=strlen(s);
          build();
          int mid=n-nxt[n-1];//要循环多少
          if(mid!=n&&n%mid==0)
               printf("0\n");
          else
               printf("%d\n",mid-nxt[n-1]%mid);
     }
     return 0;
}

-------------------------这样以后--------------------------------基本上都是-------------------------------------板子------------------------------------了

呼呼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值