标签:ati 为什么 style 连接 依次 解决 int 前缀 http
题目描述
给出一个字符串,求它的最小循环节。
思路
之前讲过Hash的做法,不过这也是KMP的模板题。
我们有结论:若n%(n - p [ n ])==0,最小循环节长度为n/(n - p [ n ]);否则就为它本身。
我们对着证明考虑两部分,一是可以整除时为什么这是答案,而是为什么不能整除是最小循环节为它本身。
①如果n是(n - p [ n ])的倍数,我们可以把字符串分为若干段长(n - p [ n ])的字符串。首先我们把每段记为A、B、C、D(假设只有四段),所以D段表示的是字符串S[ p[ n ],n ]这一段,而由P数组的定义我们可知p[ n ]表示的n的最长公共前后缀,即S[ 1 ...p[ n ] ]前缀和S[ p[ n ] ...n ]后缀相同,所以我们有ABC=BCD(表示字符串依次连接),由于它们完全相等,所以A=B,B=C,C=D,所以可知A就是循环节,而很容易由反证法知道不存在更短的循环节,否则和p数组定义不符。其数目即为答案。
②我们需要解决为什么不整除时就最小循环节就是它本身。我们运用反证法,假设存在最小循环节长len(len!=n),那么S[ 1,len ]=S[ n-len,n ],那么len=n - p[n],而由假设知,n不被n - p [ n ]整除,但又由循环节定义知n被len整除,因此矛盾。
代码
#include <bits/stdc++.h> using namespace std; const int MAXN=1e6+10; int pre[MAXN]; char s[MAXN]; int main() { while(~scanf(" %s",s+1)) { int n=strlen(s+1); if(s[1]==‘.‘)break ; int j=0;pre[1]=0; for(int i=1;i<n;i++) { while(j>0&&s[i+1]!=s[j+1])j=pre[j]; if(s[i+1]==s[j+1])j++; pre[i+1]=j; } if(n%(n-pre[n])==0)printf("%d\n",n/(n-pre[n])); else printf("1\n"); } return 0; }
标签:ati 为什么 style 连接 依次 解决 int 前缀 http
原文地址:https://www.cnblogs.com/fangbozhen/p/11623004.html