【HDU3746】【KMP】Cyclic Nacklace 最少添加字符数使得原串出现循环节

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned int UI;
typedef int Int;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
using namespace std;
const int N=1e5+10;
int casenum,casei;
char s[N];
int nxt[N];
int main()
{
	scanf("%d",&casenum);
	for(casei=1;casei<=casenum;casei++)
	{
		scanf("%s",s);
		int len=strlen(s);
		int j=-1;
		nxt[0]=-1;
		for(int i=1;i<len;i++)
		{
			while(j>=0&&s[j+1]!=s[i])j=nxt[j];
			if(s[j+1]==s[i])j++;
			nxt[i]=j;
		}
		int match=nxt[len-1]+1;
		int ans;
		if(match==0)ans=len;
		else
		{
			int cir=len-match;
			int rst=len%cir;
			ans=rst?cir-rst:0;
		}
		printf("%d\n",ans);
	}
	return 0;
}
/*
【题意】
T(100)组数据。
对于每组数据,给你一个长度为[3,1e5]的串,
我们希望把这个串在头或尾添加最少个数的字符,使得这个串成为恰好循环至少2次的串。
问你这个最小添加的字符的数量是多少。

【类型】
KMP

【分析】
问题1,
在头或尾添加字符,使这个串恰好循环至少2次。
难道我们要考虑在头添加多少个,在尾添加多少个么?
不!因为循环性质,所以其实我们只用在尾操作就好。

问题2,
能否找到一些关于循环节的特征属性呢?
我们发现,因为循环节贯穿字符串的每个位置,所以自然包含其头和尾。
这道题的最坏情况下,答案为字符串原始长度len。
而在什么条件下,答案不为len呢?
很显然,条件是,这个字符串的头与尾存在匹配。
而且因为循环节的延展性,所以一定严格有,最前的num个与最后的num个相匹配。
而最大条件下的num,我们可以通过KMP,在O(n)的时间复杂度内求得。
即,num就是KMP所求出来的nxt[len-1]+1;
我们有,s[0~num-1]=s[len-num~len-1].

然后这里就对应到两种情况——
情况1,num-1<len-num.
这种情况下,前后匹配串不交叉。答案就是(len-num)-(num-1)-1,即中间长度。
情况2,num-1>=len-num.
这种情况下,前后匹配串交叉。我么这里就可以得到,最小循环节为长度为len-num。
如果存在其他循环节,也必然是这个最小循环节长度的倍数。
而len%(len-num)之后的长度,都是多出来的。
如果多出来的长度为0,那么原始串就恰好循环;
如果多出来的长度不为0,那么我们至少添加的长度就为(len-num)-len%(len-num);

而我们恰好发现,按照如下代码实现,恰好也可以覆盖掉情况1.
int cir=len-match;
int rst=len%cir;
ans=rst?cir-rst:0;
因为不论是否前后匹配相交叉,
cir都会是去掉匹配长度后的剩余串长,
然后在实际上也恰好等于循环长度。
然后用len%循环长度,就能得到相应的最少添加字符数。

*/

你可能感兴趣的:(KMP,ACM,ICPC,HDU)