kmp
最小表示算法。。。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmath> using namespace std; const int maxlen=1e6+5; template<typename T> inline void read(T &a){ a=0;T b=1;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=-1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } a*=b; } char ch[50]; int temp; template<typename T> inline void print(T a){ if(a<0){ putchar('-'); a=-a; } do{ ch[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(ch[temp--]); } char s[maxlen]; int next[maxlen],len; inline void kmp_init(){ int p=0; for(int i=2;i<=len;i++){ while(s[p+1]!=s[i] && p)p=next[p]; if(s[p+1]==s[i])p++; next[i]=p; } } int kmp(int p){ //递归求解最小表示长度 奇数个or偶数个 if(!next[p])return p; if(next[p]*2==p){ return kmp(next[p]); } else if( !(p%(p-next[p]*2)) ){ int ans=kmp(p-next[p]); return !(p%ans) ? ans : p; } else return p; } inline int judge(int pp){ int ans=0,p=0,last=0; for(int i=1;i<=len;i++){ while(s[p+1]!=s[i]&&p)p=next[p]; if(s[p+1]==s[i])p++; if(i-p!=last)break; if(p==pp){ ans++; p=next[p]; last=i; } } return ans; } int main(){ while(gets(s+1)!=NULL){ memset(next,0,sizeof(next)); len=strlen(s+1); kmp_init(); if(next[len]){ if(len%(len-next[len])==0)print(len/(len-next[len]));
//告诉我们一个道理:next[i]不一定<(i/2),瞬间变得简单了 else print(1); } else print(1); putchar('\n'); } return 0; }
zoo:
分析一下,为什么一直TLE,没有想到o(n)算法,自己也是加了好多优化
由于自己没有把next数组用上,kmp在 初始化 时&&匹配 时使用next数组,
自己只在初始化用了,匹配没用,而且一直想怎么快点暴力而不是换个角度,
分析结束。!!!把一个问题想到非常清晰再写,包括复杂度,具体流程!!!
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmath> #define LL long long using namespace std; const int P=1000000007; const int maxn=1e6+5; int n,len,next[maxn]; LL ans; char s[maxn]; inline void kmp_init(){ len=strlen(s+1); ans=1ll; int p=0; for(int i=2;i<=len;i++){ while(s[p+1]!=s[i]&&p)p=next[p]; if(s[p+1]==s[i])p++; next[i]=p; } } int main(){ scanf("%d",&n); while(n--){ scanf("%s",s+1); kmp_init(); for(int i=1;i<=len;i++){ int j=next[i]; LL now=0; while(j){ if(j%(j-next[j])==0){//可以最小表示 int l=j-next[j];//循环串长度 now+=min(j/l,i/2/l); break; } if(j*2<=i)now++; j=next[j]; } //printf("%d ",now); ans=ans*(now+1)%P; } printf("%lld\n",ans); } return 0; }
kmp其实有一步是自己和自己匹配,所以
可以考虑 自己匹配自己 而不是直接暴跳next数组
std:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define LL long long using namespace std; template <typename T> inline void read(T &a){ a=0;T b=1;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=-1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } a*=b; } char ch[50]; int Temp; template<typename T > inline void print(T a){ if(a<0){ putchar('-'); a=-a; } do{ ch[++Temp]=a%10+'0'; a/=10; }while(a); while(Temp)putchar(ch[Temp--]); } const int maxlen=1e6+5; const LL P=1000000007; int n,len,next[maxlen]; LL deep[maxlen]; char a[maxlen]; inline void kmp_init(){ //memset(next,0,sizeof(next)); //memset(deep,0,sizeof(deep)); len=strlen(a+1); int p=0;//next[1]=0; deep[1]=1ll; for(int i=2;i<=len;i++){ while(p&&a[p+1]!=a[i])p=next[p]; if(a[p+1]==a[i])p++; next[i]=p; //print(next[i]);print(i);putchar('\n'); deep[i]=deep[p]+1; //deep[i]表示i位置能回跳的步数 } } LL ans; int main(){ read(n); while(n--){ scanf("%s",a+1); kmp_init(); ans=1; int p=0; for(int i=2;i<=len;i++){ while(p&&a[p+1]!=a[i])p=next[p]; //记得kmp就是自己和自己(别人)匹配的过程 if(a[p+1]==a[i])p++; while((p<<1)>i)p=next[p]; ans=(ans*(deep[p]+1))%P; } print(ans);putchar('\n'); } return 0; }