题意:
桌子上有 n 个石头围成一个环。每个石头都有一种颜色。每种颜色可以由小写英文字母表示。如果每一对相邻的石头都是不同颜色的,则称这 n 个石头构成的环是美丽的。现在,你可以从这 n 个石头中拿走一段连续的石头(可以为空),且你只能拿一次。
对于每个 k(0≤k≤n−1) ,判断是否存在一种取石头的方案,使得在拿走 k 个连续的石头后,剩下的 n−k 个石头构成的环是美丽的。
n<=1000000
正解kmp,转换思路,不去考虑拿走什么,考虑留下什么,不去考虑什么合法,去考虑哪些是违法的。
显然如果a[i]=a[i+1],a[i]与a[i+1]一定不能同时留下,所以我们可以将序列切成很多段,在每一段中,如果留下长度k不合法,那么说明k-1是它的一个循环节,因为不合法只能是头尾相同,即a[i]=a[i+k-1],如果对于每个位置都满足,那么说明k-1是串的一个循环节(不要求乘除)。
每一段求完再求个并就好了。
next数组一定要从1开始!!!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define N 1000005 6 using namespace std; 7 char s[N*2]; 8 int nxt[2*N],ans[2*N]; 9 int n; 10 void solve(int l,int r) 11 { 12 nxt[1]=0;int k=0; 13 for(int i=l+1;i<=r;i++) 14 { 15 while(k&&s[k+l]!=s[i])k=nxt[k]; 16 if(s[k+l]==s[i])k++; 17 nxt[i-l+1]=k; 18 } 19 int len=(r-l+1); 20 for(int i=1;i<=len;i++)ans[i]++; 21 while(k) 22 { 23 ans[len-k+1]--; 24 k=nxt[k]; 25 } 26 return ; 27 } 28 int main() 29 { 30 int tot=0; 31 while(~scanf("%s",s)) 32 { 33 n=strlen(s); 34 memset(ans,0,sizeof(ans)); 35 memset(nxt,0,sizeof(nxt)); 36 for(int i=0;i<n;i++)s[i+n]=s[i]; 37 int pre=0; 38 for(int i=1;i<2*n;i++) 39 { 40 if(s[i]==s[i-1]) 41 { 42 solve(pre,i-1); 43 pre=i; 44 } 45 } 46 solve(pre,2*n-1); 47 printf("Case %d: ",++tot); 48 for(int i=0;i<=n-1;i++) 49 { 50 if(ans[n-i])putchar('1'); 51 else putchar('0'); 52 } 53 puts(""); 54 } 55 return 0; 56 }