第2部分 字符串算法(提高篇)--第2章 KMP算法1466:【例题2】Power Strings

  • 1466:【例题2】Power Strings

时间限制: 1000 ms 内存限制: 65536 KB
提交数: 1013 通过数: 497
【题目描述】
原题来自:POJ 2406

给定若干个长度 ≤10^6 的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的。如:ababab 则最多有 3 个 ab 连接而成。

【输入】
输入若干行,每行有一个字符串,字符串仅含英语字母。特别的,字符串可能为 . 即一个半角句号,此时输入结束。

【输出】
【输入样例】
abcd
aaaa
ababab
.
【输出样例】
1
4
3

第2部分 字符串算法(提高篇)--第2章 KMP算法1466:【例题2】Power Strings_第1张图片

思路:
一个字符存在循环节,当且仅当它的长度 len能被 len−next[len]整除,并且它的最短循环节就是下标在区间 [1,len−next[len]]内的它的子串(下标从 1 开始)
next指的是 kmp 中用到的那个 next数组。
实质是让字符串自己匹配自己是一样的。
让字符串自己匹配自己,第一次匹配上一定是自己的前缀匹配到自己的后缀
图中红色部分都是相同的子串。显然它们会最先被匹配到。
图中每个斜线部分都是一个循环节,每个循环节都是相等的。

因为上面1那条绿色的开头长度为 len−next[len]的那一段和下面2那条开头长度为 len−next[len]的那段必然是相同的(因为上下两条是相同的字符串),而且上面1那条的的第二段和下面2那条的第一段是相同的(因为你进行了 kmp 匹配,中间长度为 next[len]的那一段上下两条都是相同的),所以上一条的第一段和第二段是相同的。
以此类推,就能发现开头长度为 len−next[len]的那一段是最短的循环节。
因此对于这题我们只要跑一遍 kmp 求出 next 数组再根据定理求出答案即可。

#include
#include
#include
#include 
using namespace std;
char s[1000005];
int next[1000005];
void getnext(char *a)
{
 int len=strlen(a);
 int i=0,j=-1;
 next[0]=-1;
 while(i<len)
 {
 	if(j==-1||a[i]==a[j])
 	 next[++i]=++j;
 	else j=next[j];
 }
}
int main()
{
 while(scanf("%s",s)!=EOF&&s[0]!='.')
 {
 	int len=strlen(s);
 	getnext(s);
 	if(len%(len-next[len])==0)
 	 printf("%d\n",len/(len-next[len]));
 	else
 	 printf("1\n");
 }
 return 0;
}

你可能感兴趣的:(信息学C++,一本通)