标签:字符 更新 数字 dig 后缀 scan lin oid max
题意:给一个数字,每一次把它的最后一位拿到最前面,一直那样下去,分别求形成的数字小于,等于和大于原来数的个数。
SAM乱搞失败
当然要先变SS了
然后考虑每个后缀前长为n个字符,把它跟S比较就行了
如果用后缀家族的话复杂度要加上log,本题会TLE吧
求一个串S的每个后缀与另一个串T的最长公共前缀可以用扩展KMP!复杂度O(n+m)
看课件吧
从1开始写真不容易以后再也不从1开始了,判断位置好麻烦好容易错
next[i]=LCP(T[i,m],T)
extend[i]=LCP(S[i,n],T)
主要思想就是求i时记录p=max{a+extend[a]-1},1<=a<i 也就是当前最远匹配到哪里,这么做是为了以后直接利用next的信息
S[a,a+extend[a]-1]--->T[1,extend[a]-1]是一样的
那么从i开始的后缀的前一部分是和T[i-a+1..]相同的,就不用比较了因为已经有L=next[i-a+1]了
如果i+L-1<p,extend[i]=L
否则从s[p+1]和t[p-i+1+1]开始暴力往后匹配,并更新a
注意:本题要求不相同的数字,出现相同数字说明有循环节,KMP求一下看看有没有可以整除的循环节
#include <cstdio> #include <cstring> using namespace std; const int N=2e6+5; inline int read(){ char c=getchar();int x=0,f=1; while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘; c=getchar();} return x*f; } inline int min(int a,int b){return a<b?a:b;} inline int max(int a,int b){return a>b?a:b;} int n,m; int next[N],extend[N]; char s[N],t[N]; void getNext(char s[],int n){ //printf("getNext %d\n",n); //for(int i=1;i<=n;i++) printf("%c",s[i]); next[1]=n; int a=1; while(a<n&&s[a]==s[a+1]) a++; next[2]=a-1; a=2; for(int i=3;i<=n;i++){ int p=a+next[a]-1,L=next[i-a+1]; if(i+L-1<p) next[i]=L; else{ int j=max(p-i+1,0);//!! while(i+j<=n&&s[i+j]==s[j+1]) j++; next[i]=j;a=i; } } //for(int i=1;i<=n;i++) printf("next %d %d\n",i,next[i]); } void getExtend(char s[],char t[]){ //printf("getExtend %d\n",n); //for(int i=1;i<=n;i++) printf("%c",s[i]);puts(""); getNext(t,m); int mn=min(n,m),a=1; while(a<=mn&&s[a]==t[a]) a++; extend[1]=a-1; a=1; for(int i=2;i<=n;i++){ int p=a+extend[a]-1,L=next[i-a+1]; if(i+L-1<p) extend[i]=L; else{ int j=max(p-i+1,0); while(i+j<=n&&s[i+j]==t[j+1]) j++; extend[i]=j; a=i; } } //for(int i=1;i<=n;i++) printf("extend %d %d\n",i,extend[i]); } int fail[N]; void getFail(char s[],int n){ fail[1]=0; for(int i=2;i<=n;i++){ int j=fail[i-1]; while(j&&s[j+1]!=s[i]) j=fail[j]; fail[i]=s[j+1]==s[i]?j+1:0; } } int L,E,G; void solve(){ getExtend(s,t); L=E=G=0; for(int i=1;i<=m;i++){ int lcp=extend[i]; if(lcp>=m) E++; else if(t[1+lcp]<s[i+lcp]) G++; else L++; } //for(int i=1;i<=n;i++) printf("hi %d %d %d\n",i,next[i],extend[i]); getFail(t,m); int _=m%(m-fail[m])==0?m/(m-fail[m]):1; L/=_;E/=_;G/=_; } int main(){ freopen("in","r",stdin); int T=read(),cas=0; while(T--){ scanf("%s",s+1); n=m=strlen(s+1); for(int i=1;i<=n;i++) s[i+n]=t[i]=s[i]; n<<=1; solve(); printf("Case %d: %d %d %d\n",++cas,L,E,G); } }
HDU 4333 Revolving Digits [扩展KMP]【学习笔记】
标签:字符 更新 数字 dig 后缀 scan lin oid max
原文地址:http://www.cnblogs.com/candy99/p/6395066.html