【ZROI2018暑假刷题营D3T2绕口令】
orz 从来都是背板子,从来没有认真思考过其背后的真实含义。
YJC最近在研究绕口令。为了方便,假设绕口令是一个由小写字母组成的字符串。
好的绕口令中,任意两个相邻的字符都不相同。因为绕口令要不停的念,第一个和最后一个字符也不能相同(长度为1除外)。
现在YJC有一个长度为n的由小写字母组成的字符串,他想知道,对于∀0≤k<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(""); } }