【ZROI2018暑假刷题营D3T2绕口令】

orz 从来都是背板子,从来没有认真思考过其背后的真实含义。 YJC最近在研究绕口令。为了方便,假设绕口令是一个由小写字母组成的字符串。 好的绕口令中,任意两个相邻的字符都不相同。因为绕口令要不停的念,第一个和最后一个字符也不能相同(长度为1除外)。 现在YJC有一个长度为n的由小写字母组成的字符串,他想知道,对于0k<n   ,能否从字符串中删去连续k个字符,使得剩下的串是一个好的绕口令。

输入格式

包含多组数据。 每组数据包含一行,一个字符串,表示初始字符串。

输出格式

对于每组数据,在一行内输出结果"Case T:result"(不输出引号)。其中T为当前是第几组数据,result为一个长度为n的01 串,第k位(从0开始)为1表示能从字符串中删去连续k个字符,使得剩下的串是一个好的绕口令,为0则表示不能。

样例1

样例输入

rrg
rrrrr
brbg
abab

样例输出

Case 1:011
Case 2:00001
Case 3:1111
Case 4:1011

样例2

见下载文件

限制

对于50%的数据,保证所有字符串总长不超过5000。 对于100%的数据,保证所有字符串总长不超过1000000。 时间限制:2s 内存限制:512MB

解法

这道题由于是个环,我们直接倍长来考虑。然后由于是删除k,我们考虑保留n-k长度的串的方式考虑。如果在原串中有相邻的两个字母我们就把他们隔开(显然他们不能保留在同一个串里面作为好串),这样就把原串分成了若干段。接下来我们需要考虑的就是在一个段里面长度为x的所有段是不是都不能作为好串,这时候我们考虑判断前后缀相同的长度即可。这里用KMP或者hash就可以了。 什么?你说KMP是用来跑匹配的?我开始也是这么想的。其实如果我们对一个串本身跑kmp,那么就最后一定有len-nt[len]+1和len-nt[nt[len]]+1......一直下去这么多长度的串不可以,在这个情况下前缀和后缀相同。这里就是利用的kmp的next数组(同时next数组也可以判断一个串的最小循环节),这个不懂还是重看一下kmp吧,orz orz orz 。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn = 2000005;
char ss[maxn],st[maxn];
int n,len,cc; int nt[maxn];
bool ok[maxn],ans[maxn];
void solve()
{
	for(int i=1;i<=cc;i++) ok[i]=1,nt[i]=0;
	int j = 0;
	for(int i=2;i<=cc;i++)
	{
		if(j&&st[j+1]!=st[i]) j=nt[j];
		if(st[j+1]==st[i]) j++;
		nt[i]=j;
	}
	for(int i=nt[cc];i;i=nt[i]) ok[cc-i+1]=0;
	for(int i=1;i<=min(cc,n);i++) {if(ok[i]) ans[i]=1; st[i]=0; nt[i]=0; st[i]=' '; }
	//min(cc,n)很重要!!! 
}
int main()
{
	int T=0;
	while(~scanf("%s",ss+1))
	{
		++T; printf("Case %d:",T);
		n=strlen(ss+1);
		len=n*2-1;
		for(int i=1;i<n;i++) ss[i+n]=ss[i];
		for(int i=1;i<=len;i++)
		{
			st[++cc]=ss[i];
			if(ss[i]==ss[i+1]) 
			{
				solve(); cc = 0;
			}
		}
		if(cc) solve(); cc=0;
		for(int i=0;i<n;i++) printf("%d",ans[n-i]);
		for(int i=0;i<=n;i++) ans[i]=0;
		puts("");
	}
}
 
posted @ 2018-08-07 20:46  Newuser233  阅读(11)  评论(0)    收藏  举报