hdu 4333 扩展kmp+kmp重复字串去重
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4333
关于kmp next数组求最短重复字串问题请看;http://www.cnblogs.com/z1141000271/p/7406198.html
扩展kmp请看:http://www.cnblogs.com/z1141000271/p/7404717.html
题目大意:一个数字,依次将第一位放到最后一位,问小于本身的数的个数及等于本身的个数和大于本身的个数,但是要注意重复的不再计算
题解:我们把得到的串double一下(比如s=aa double之后 s=aaaa),然后对double之后得到的串做一个次扩展kmp,然后便利得到的Next数组。如果公共前缀匹配>=原串长度则e++,对于公共前缀小于原串长度的,我们去比较失配的第一位就可以了。再就是去重的问题,这个要注意!如果原串可以由更小的串重复得到,那我们计算的结果就有很多重复的。
(注意double 以及去重两个操作的使用)
ac代码:
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #define mt(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const ll mod=1e9+7; int extend[200001]; int Next[200001]; int min(int x,int y) { if(x>y) return y; return x; } int knext[200005]; void getknext(char s[],int len) { mt(knext); int i=0; int j=-1; knext[i]=j; while(i<len) { if(j==-1 || s[i]==s[j]) knext[++i]=++j; else j=knext[j]; } } void getNext(char t[],int len) { mt(Next); //ll len=strlen(t); Next[0]=len; int a,p; a=1; while( a<len && t[a]==t[a-1]) a++; // 求出长度为1的时候 解为多少 Next[1]=a-1; a=1; for(int i=2;i<len;i++) // 后续的按照算法来就好 { p=a+Next[a]-1; if((i-1)+Next[i-a] < p ) Next[i]=Next[i-a];// 第一种情况 没有超过等于的部分 else // 超过的话就不好直接用next的定义 需要后续的遍历 { ll j = (p - i + 1) > 0 ? (p - i + 1) : 0; while(i + j < len && t[i+j] == t[j]) j++; Next[i]=j; a=i; } } } char s[2000005];// s->exkmp t->Next int main() { int Case; scanf("%d",&Case); for(int z=1;z<=Case;z++) { scanf("%s",s); int len=strlen(s); int j=0; int l,e,g; getknext(s,len); int temp=len-knext[len]; // 最小重复长度 if(len%temp) temp=1; else temp=len/temp; for(int i=0;i<len;i++) { s[len+i]=s[i]; } getNext(s,2*len); l=e=g=0; for(int i=0;i<len;i++) { if(Next[i]>=len) e++; else { int temp=i+Next[i];// postion of not match if(s[temp]<s[Next[i]]) l++; else if(s[temp]>s[Next[i]])g++; } } printf("Case %d: %d %d %d\n",z,l/temp,e/temp,g/temp); } return 0; }