FZU 1901 最小循环节

26 篇文章 0 订阅

Problem 1901 Period II

Accept: 676    Submit: 1825
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

For each prefix with length P of a given string S,if

S[i]=S[i+P] for i in [0..SIZE(S)-p-1],

then the prefix is a “period” of S. We want to all the periodic prefixs.

 Input

Input contains multiple cases.

The first line contains an integer T representing the number of cases. Then following T cases.

Each test case contains a string S (1 <= SIZE(S) <= 1000000),represents the title.S consists of lowercase ,uppercase letter.

 Output

For each test case, first output one line containing "Case #x: y", where x is the case number (starting from 1) and y is the number of periodic prefixs.Then output the lengths of the periodic prefixs in ascending order.

 Sample Input

4oooacmacmacmacmacmafzufzufzufstostootssto

 Sample Output

Case #1: 31 2 3Case #2: 63 6 9 12 15 16Case #3: 43 6 9 10Case #4: 29 12

 Source

FOJ有奖月赛-2010年05月

跑一边len - Next[j] ,j=Next[j]

原理是什么呢  性质2:kmp的next不断向前递归的过程可以保证对于每一个当前前缀,都有一段后缀与之对应

那么你进行递归的时候其实已经是头尾一样 恒匹配 也就可以拿去对应匹配

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
using namespace std;
const int MAX_N = 1000024;
int Next[MAX_N];
char mo[MAX_N];
int ans[MAX_N];
int n2;
void getnext(){
   int i = 0,j = -1;
   while(i<n2){
    if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i] = j;}
    else j = Next[j];
   }
   return ;
}
int main(){
   int t;
   scanf("%d",&t);
   for(int k = 1;k<=t;k++){
    memset(ans,0,sizeof(ans));
    //memset(Next,0,sizeof(Next));
    int cnt = 0;
    scanf("%s",mo);
    n2 = strlen(mo);
    Next[0] = -1;
    getnext();
    int j = n2;
    //ans[cnt++]=j-Next[j];
    while(Next[j]!=0){
        ans[cnt++] =n2-Next[j];
        j = Next[j];
    }
    ans[cnt++] = n2;
    printf("Case #%d: %d\n",k,cnt);
   for(int i = 0;i<cnt;i++){
    i==cnt-1?printf("%d\n",ans[i]):printf("%d ",ans[i]);
   }
   }
   return 0;
}

这个人写的挺好的:

对于所有的方案,可以归结于以下几类(实际上就是一类,我们画图了而已)

 

首先,next[len]表示,从[0,  next[len]])的区域和串最后面[len-next[len], next[len]  )区域的字符串是一样的。(len表示当前串长度)

 

对于一个长度为13的串,next[13],显然就表示前面几个字母,和后面几个字母相同。 在图上就是s[0]...s[3] 和s[9]..s[12]相同。

显然P=9成立,因为s[0]=s[0+9]  s[1]=s[1+9] s[2]=s[2+9] s[3]=s[3+9] 然后s[4]配对的时候,不存在s[4+9=13]13这个元素。

 

所以我们可以先通过这个状态,得到第一个合法解:P=9 (len - next[len])

 

经过next[4]=2,可以知道s[0]s[1] 和s[2]s[3]相同。

 

又因为s[0]..s[4]和s[9]..s[12]相同,所以s[0],s[1]和s[11],s[12] 相同。

从而得到P=11的解。 11 = len - next[4]

 

再然后显然,就是求next[2]了,因为这表示s[0] s[1]区间前缀和后缀相同的情况,后缀又等于整个串的后缀。

假设s[0]==s[1]的话,next[2]就是1

得到P=12    12=len-next[2]

然后next[1]=0,实际上已经算法结束了。这个时候的前缀等于后缀的部分已经是空集了。

 

对于next[len]是下图情况:

 

有交集部分。 显然s[0]....s[8]  和s[5]...s[13]是相同。

s[0]=s[5], s[1]=s[5]....s[i]=s[i+5] 

得到一个答案P=5    LEN - next[LEN] = 14-9=5

 

下面情况略多。先看图上面的情况。(next[9]=6)

s[0]=s[3]   ->    s[0]=s[5]=s[8]=s[8]  (因为next[14]=9,看蓝色的圈,他们是等价的,两个蓝色的圈是相同的对不对,然后左边蓝色圈里的首位相同,肯定右边蓝色圈里首位也相同。两个蓝色圈又相同,所以可以得到这样的式子)

s[0] = s[8]

s[1] = s[9]

...

得到的P=8.    LEN - next[9] = 14-6=8。

 

对于图下面的情况也是类似,next[9]=4.

同时s[0]显然可以等于s[10],(第一个红圈和第三个红圈彼此等价)从而得出解, p = 14-4=10

 

然后递归下去,每一个next所指的字符串区域相同的前缀(从s[0]开始)和后缀,都可以对应上原串的前缀(从s[0]开始)和后缀(s[len-1]结尾)。

 

 

回到第一张图

对于所有的P,大致也就是这些情况了。我们需要证明,刚才上述的算法,能包含所有的情况。

 

对于上图所示的情况,

实际上是这样的。两个橘黄色是相同的。然后就回到了上面的状态了。所有的子状态都是可达的。所以这题就做完了。

先next[13]得到9

然后就是next[9]=4。

看起来只得到的P=9的情况,没有P=4的情况。

实际上漏了第一步……就是13-next[13]的情况…… 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值