hdu4333 扩展KMP
慢慢研究可以发现,可以用扩展kmp来求。由于扩展kmp的next[]只有一部分,当前位子前面那部分和母串的后部分,所以可以将字符串复制接在后面一次。
先求如果next[]>0&&next[]!=len,那么只要考虑后面那位的大小比较。如果next[]>=len 那就是相同。如果next[]==0,就是没有相同的,直接比较开头。
这样做还是会超时,我tle无数发。
还要去掉重复的部分,题目要求不同的。所以求出循环部分,只要考虑一部分即可。
//扩展kmp求最小循环节 int kk; // kk保存最短循环节 for(i=1; i<=len; ++i) { if(i+next[i]>=len) { kk = len%i ? len : i; break; } }
#include<stdio.h> #include<string.h> #define maxn 100010 char s[maxn],ss[maxn*2]; int next[maxn*2],n[maxn]; void getnext() { int i,j,len=strlen(ss),k; next[0]=len; i=0; while(i<len-1&&ss[i]==ss[i+1]) i++; next[1]=i; int a=1; for(k=2;k<len;k++) { int p=a+next[a]-1; int l=next[k-a]; if(k-1+l>=p) { int j=p-k+1>0?p-k+1:0; while(j+k<len&&ss[j+k]==ss[j]) j++; next[k]=j; a=k; } else next[k]=l; } } void Ekmp() { getnext(); int i,j,len=strlen(s),k; int ansmin,ansequ,ansmax; ansmin=ansequ=ansmax=0; //扩展kmp求最小循环节 int kk; // kk保存最短循环节 for(i=1; i<=len; ++i) { if(i+next[i]>=len) { kk = len%i ? len : i; break; } } for(i=0;i<kk;i++) { if(next[i]==0) { if(ss[i]>ss[0]) ansmax++; else ansmin++; } else if(next[i]>=len) { ansequ++; } else { if(ss[next[i]+i]>ss[next[i]]) { ansmax++; } else ansmin++; } } printf("%d %d %d\n",ansmin,ansequ,ansmax); } int main() { int i,j,t,ff=0,len; scanf("%d",&t); while(t--) { scanf("%s",s); len=strlen(s); int k=0; for(i=0;i<len;i++) ss[k++]=s[i]; for(i=0;i<len;i++) ss[k++]=s[i]; ss[k]='\0'; printf("Case %d: ",++ff); Ekmp(); } }