字符串相关模板
KMP、AC自动机、后缀数组、后缀自动机、manacher、回文自动机
KMP
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=1e6+10; int len1,len2,nt[maxn]; char s[maxn],c[maxn]; int main() { scanf("%s%s",s+1,c+1); len1=strlen(s+1);s[len1+1]='#'; len2=strlen(c+1);c[len2+1]='#'; int pos; for(int i=2;i<=len2;++i) { pos=nt[i-1]; while(pos&&c[pos+1]!=c[i]) pos=nt[pos]; nt[i]= c[pos+1]==c[i]? pos+1:0; } pos=0; for(int i=1;i<=len1;++i) { while(pos&&c[pos+1]!=s[i]) pos=nt[pos]; if(c[pos+1]==s[i]) pos++; if(pos==len2) printf("%d\n",i-len2+1),pos=nt[pos]; } for(int i=1;i<=len2;++i) printf("%d ",nt[i]); return 0; }
AC自动机(bzoj3530)
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long const int maxn=1500+7; const ll mod=1e9+7; int n,m; char s[maxn],d[maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } void mo(ll &x){if(x>=mod) x-=mod;} int fail[maxn],son[maxn][11],has[maxn],tot; void insert() { int len=strlen(s+1),now=0,x; for(int i=1;i<=len;++i) { x=s[i]-'0'; if(!son[now][x]) son[now][x]=++tot; now=son[now][x]; } has[now]=1; } int zz[maxn]; void bld() { int s=1,t=0,x; for(int i=0;i<10;++i) if(son[0][i]) zz[++t]=son[0][i]; while(s<=t) { x=zz[s++]; has[x]|=has[fail[x]]; for(int i=0;i<10;++i) { if(son[x][i]) { fail[son[x][i]]=son[fail[x]][i]; zz[++t]=son[x][i]; } else son[x][i]=son[fail[x]][i]; } } } ll f[maxn][maxn][4]; ll solve(int pos,int len,int p) { if(has[pos]) return 0; if(len>n) return 1; if(~f[pos][len][p]) return f[pos][len][p]; ll rs=0; if(p&2) rs+=solve(pos,len+1,p&2); int l=p>>1,r=(p&1)? (d[len]-'0'):9; for(int i=l;i<=r;++i) { rs+=solve(son[pos][i],len+1,(p&1)&(i==d[len]-'0')); mo(rs); } return f[pos][len][p]=rs; } int main() { scanf("%s",d+1); n=strlen(d+1); read(m); for(int i=1;i<=m;++i) { scanf("%s",s+1); insert(); } bld(); memset(f,-1,sizeof(f)); printf("%lld",(solve(0,1,3)-1+mod)%mod); return 0; } /* 20 3 2 3 14 */
后缀数组、manacher(0103练习题T3,和去年省选D2T3很像,但不用本质不同,而且简单很多,用后缀数组和马拉车预处理lcp和以i为开头的回文串(放到树状数组里))
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long const int maxn=16e5+10,INF=0x3f3f3f3f; int n,m,Q,ql,qr;ll totans; char A[maxn],B[maxn],S[2*maxn]; int x[maxn],y[maxn],c[maxn],sa[maxn],h[maxn],rad[maxn]; ll aa;char cc; ll read() { aa=0;cc=getchar(); while(cc<'0'||cc>'9') cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); return aa; } ll minnum[4*maxn]; void bld(int pos,int l,int r) { if(l==r) { minnum[pos]=h[l]; return; } int mid=(l+r)>>1; bld(pos<<1,l,mid); bld(pos<<1|1,mid+1,r); minnum[pos]=min(minnum[pos<<1],minnum[pos<<1|1]); } ll qmin(int pos,int l,int r) { if(l>=ql&&r<=qr) return minnum[pos]; int mid=(l+r)>>1;ll rs=INF; if(ql<=mid) rs=min(rs,qmin(pos<<1,l,mid)); if(qr>mid) rs=min(rs,qmin(pos<<1|1,mid+1,r)); return rs; } ll tot[2][maxn],sum[2][maxn]; ll q(int pos,ll *sz) { ll rs=0; while(pos) { rs+=sz[pos]; pos-=pos&-pos; } return rs; } void chge(int pos,int x,ll *sz,int n) { while(pos<=n) { sz[pos]+=x; pos+=pos&-pos; } } void get_sa(char *s,int n) { int m='z'+1,p; for(int i=0;i<n;++i) c[x[i]=s[i]]++; for(int i=1;i<m;++i) c[i]+=c[i-1]; for(int i=n-1;i>=0;--i) sa[--c[x[i]]]=i; for(int k=1;k<=n;k<<=1) { p=0; for(int i=n-1;i>=n-k;--i) y[p++]=i; for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k; for(int i=0;i<m;++i) c[i]=0; for(int i=0;i<n;++i) c[x[y[i]]]++; for(int i=1;i<m;++i) c[i]+=c[i-1]; for(int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i]; swap(x,y);p=1; x[sa[0]]=0; for(int i=1;i<n;++i) x[sa[i]]= y[sa[i]]==y[sa[i-1]]&& ((sa[i]+k<n&&sa[i-1]+k<n&&y[sa[i]+k]==y[sa[i-1]+k])||(sa[i]+k>=n&&sa[i-1]+k>=n))? p-1:p++; if(p>=n) break; else m=p; } p=0;int u; for(int i=0;i<n;++i) { if(!x[i]) continue; u=sa[x[i]-1]; if(p) p--; while(s[u+p]==s[i+p]) p++; h[x[i]]=p; } memset(minnum,0x3f3f3f3f,sizeof(minnum)); bld(1,1,n-1); } void manacher(char *s,ll *sz,int len,int o) { int maxpos=0,p=0; for(int i=len;i;--i) {//不是从0开始而是从1开始 s[i<<1]=s[i]; s[i<<1|1]='#'; } s[0]=s[1]='#'; for(int i=2;i<=2*len;++i) { if(maxpos>i) rad[i]=min(maxpos-i,rad[2*p-i]); else rad[i]=0; while(i-rad[i]>0&&i+rad[i]<=2*len+1&&s[i-rad[i]]==s[i+rad[i]]) rad[i]++; rad[i]--; if(i+rad[i]>maxpos) p=i,maxpos=i+rad[i]; } for(int i=2;i<=2*len;++i) { chge((i-rad[i]+1)>>1,1,tot[o],len); chge((i+2)>>1,-1,tot[o],len); } } int main() { freopen("palindrome.in","r",stdin); freopen("palindrome.out","w",stdout); scanf("%s",A+1); scanf("%s%s",A+1,B+1); n=strlen(A+1); m=strlen(B+1); for(int i=1;i<=m/2;++i) swap(B[i],B[m-i+1]); for(int i=1;i<=n;++i) S[i-1]=A[i]; S[n]='#';//n不是n+1 for(int i=1;i<=m;++i) S[i+n]=B[i]; get_sa(S,n+m+1); manacher(A,tot[0],n,0); manacher(B,tot[1],m,1); for(int i=1;i<=n;++i) chge(i,q(i,tot[0]),sum[0],n+1); for(int i=1;i<=m;++i) chge(i,q(i,tot[1]),sum[1],m+1); //注意是n+1和m+1,因为可能询问到 Q=read();int xx,yy,len; for(int i=1;i<=Q;++i) { xx=read(); yy=read(); ql=x[xx-1]; qr=x[yy+n]; if(qr<ql) swap(ql,qr); ++ql; len=qmin(1,1,n+m); totans=len; totans+=q(xx+len,sum[0])-q(xx,sum[0]); totans+=q(yy+len,sum[1])-q(yy,sum[1]);//注意边界 printf("%lld\n",totans); } fclose(stdin);fclose(stdout); return 0; } /* orzAchen babaabbba baaabbb 1 9 6 */
20180314update:后缀数组以前板子没有熟练,现在用熟了放一个现在的板子:
bool xd(int a,int b,int n,int k) { if((a+k>n)||(b+k>n)) return 0; return y[a]==y[b]&&y[a+k]==y[b+k]; } void get_sa(int n,int m) { int p; For(i,1,m) c[i]=0; For(i,1,n) c[x[i]=s[i]]++; For(i,1,m) c[i]+=c[i-1]; Rep(i,n,1) sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { p=0; Rep(i,n,n-k+1) y[++p]=i; For(i,1,n) if(sa[i]>k) y[++p]=sa[i]-k; For(i,1,m) c[i]=0; For(i,1,n) c[x[i]]++; For(i,1,m) c[i]+=c[i-1]; Rep(i,n,1) sa[c[x[y[i]]]--]=y[i]; swap(x,y); p=1; x[sa[1]]=1; For(i,2,n) x[sa[i]]= xd(sa[i],sa[i-1],n,k)? p:++p; if(p>=n) break; m=p; } }
后缀自动机(bzoj2555 LCT+SAM)
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define lc son[pos][0] #define rc son[pos][1] const int maxn=1200000+7; int mask,n,m,ans; char s[maxn]; string chars; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } void gets(int mask) { scanf("%s",s); chars=s; int len=chars.length(); for(int j=0;j<len;j++) { mask=(mask*131+j)%len; char t=chars[j]; chars[j]=chars[mask]; chars[mask]=t; } } struct Lct{ int fa[maxn],son[maxn][2],w[maxn],laz[maxn],zz[maxn],t; void pd(int pos) { if(!laz[pos]) return; if(lc) laz[lc]+=laz[pos],w[lc]+=laz[pos]; if(rc) laz[rc]+=laz[pos],w[rc]+=laz[pos]; laz[pos]=0; } bool isroot(int pos) { return son[fa[pos]][0]!=pos&&son[fa[pos]][1]!=pos; } void rotate(int pos) { int x,y,p,t; y=fa[x=fa[pos]];t=isroot(x); p=son[x][1]==pos; son[x][p]=son[pos][!p]; fa[son[pos][!p]]=x; son[pos][!p]=x; fa[x]=pos; fa[pos]=y; if(!t) son[y][son[y][1]==x]=pos; } void splay(int pos) { t=0; int x,y; for(x=pos;!isroot(x);x=fa[x]) zz[++t]=x; zz[++t]=x; while(t) pd(zz[t--]); for(;!isroot(pos);rotate(pos)) { y=fa[x=fa[pos]]; if(!isroot(x)) (son[x][1]==pos)^(son[y][1]==x)? rotate(pos):rotate(x); } } void access(int pos) { for(int t=0;pos;pos=fa[pos]) { splay(pos);rc=t; t=pos; } } void lk(int x,int f) { fa[x]=f; access(f); splay(f); if(f) laz[f]+=w[x],w[f]+=w[x]; } void cut(int pos) { access(pos);splay(pos); if(lc) laz[lc]-=w[pos],w[lc]-=w[pos]; fa[lc]=0; lc=0; } }lct; struct Sam{ Sam* next[27],*par; int step; }pool[2*maxn],*root,*last; int tot; Sam* newnode(int step) { Sam *t=pool+(tot++); t->step=step; t->par=NULL; memset(t->next,0,sizeof(t->next)); return t; } void Extend(int w) { // cout<<"Extend"<<w<<": "; Sam *p=last; Sam *np=newnode(p->step+1);lct.w[tot]=1; int x,y; for(;p&&!p->next[w];p=p->par) p->next[w]=np; if(!p) { np->par=root; // cout<<"np link->root\n"; lct.lk(tot,1); } else { Sam *q=p->next[w];x=q-pool+1; if(q->step==p->step+1) { np->par=q; // cout<<"np link->q\n"; lct.lk(tot,x); } else { Sam *nq=newnode(p->step+1);lct.w[tot]=0; memcpy(nq->next,q->next,sizeof(q->next)); y=q->par-pool+1; lct.cut(x); nq->par=q->par; q->par=nq; lct.lk(x,tot); np->par=nq;lct.lk(tot-1,tot); lct.lk(tot,y); // cout<<"nq linked by q&np\n"; for(;p&&p->next[w]==q;p=p->par) p->next[w]=nq; } } last=np; } void add() { gets(mask); int len=chars.length(); for(int i=0;i<len;i++) Extend(chars[i]-'A'); } int q(){ gets(mask);int x; int len=chars.length();Sam *now=root; for(int i=0;i<len;i++) if(!(now=now->next[chars[i]-'A'])) return 0; lct.splay(x=now-pool+1); return lct.w[x]; } int main() { read(m); scanf("%s",s+1); n=strlen(s+1); last=root=newnode(0); for(int i=1;i<=n;++i) Extend(s[i]-'A'); for(int i=1;i<=m;++i) { scanf("%s",s); if(s[0]=='A') add(); else { ans=q(); printf("%d\n",ans); mask^=ans; } } return 0; } /* 4 A ADD BBABBBBAAB QUERY ABA ADD BBABAB QUERY BAAB */
广义SAM(bzoj3926)
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=1e5+7,maxt=20*maxn,maxs=13; int n,c,col[maxn],ro; ll ans; char cc; ll ff; template<typename T>void read(T& aa) { aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int fir[maxn],nxt[2*maxn],to[2*maxn],e=0,ind[maxn]; void add(int x,int y) { to[++e]=y;nxt[e]=fir[x];fir[x]=e; to[++e]=x;nxt[e]=fir[y];fir[y]=e; ++ind[x];++ind[y]; } struct Node{ int col,son[maxs]; }node[maxt]; int tt; void bld(int &pos,int x,int f) { if(!pos) { pos=++tt; node[pos].col=col[x]; } int y,z; for(y=fir[x];y;y=nxt[y]) { if((z=to[y])==f) continue; bld(node[pos].son[col[z]],z,x); } } struct Sam{ Sam *par,*next[maxs]; int step;bool vis; }pool[2*maxt],*root; int tot; Sam* newnode(int step) { Sam *t=pool+(tot++); t->step=step; t->par=NULL; memset(t->next,0,sizeof(t->next)); return t; } Sam* Extend(Sam *last,int w) { Sam *p=last; Sam *np=newnode(p->step+1); for(;p&&!p->next[w];p=p->par) p->next[w]=np; if(!p) np->par=root; else { Sam *q=p->next[w]; if(q->step==p->step+1) np->par=q; else { Sam *nq=newnode(p->step+1); memcpy(nq->next,q->next,sizeof(q->next)); nq->par=q->par; q->par=nq; np->par=nq; for(;p&&p->next[w]==q;p=p->par) p->next[w]=nq; } } return np; } void dfs(int pos,Sam *p) { int z; For(i,1,c) if((z=node[pos].son[i])!=0){ Sam* t=Extend(p,i); dfs(z,t); } } int main() { read(n); read(c); For(i,1,n) read(col[i]),++col[i]; int x,y; For(i,1,n-1) { read(x); read(y); add(x,y); } For(i,1,n) if(ind[i]==1) bld(node[ro].son[col[i]],i,0); root=newnode(0); dfs(ro,root); Sam* p; For(i,1,tot-1) { p=pool+i; ans+=p->step-p->par->step; } printf("%lld",ans); return 0; } /* 7 3 0 2 1 2 1 0 0 1 2 3 4 3 5 4 6 5 7 2 5 */
ud20180613:似乎以前写了个假的广义SAM?放一个现在的板子(bzoj3277)
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=2e5+7; int n,m,K,len,L[maxn],R[maxn]; ll ans; char s[maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } struct Sam{ Sam *next[27],*par; int step,right,cur;ll w; }pool[2*maxn],*root,*last,*node[2*maxn]; int tot,t; Sam *newnode(int step) { Sam *o=pool+(tot++); memset(o->next,0,sizeof(o->next)); o->par=NULL; o->step=step; o->right=o->cur=o->w=0; return o; } void extend(int w) { Sam *p=last; if(p->next[w]) { Sam *q=p->next[w]; if(q->step!=p->step+1) { Sam *nq=newnode(p->step+1); memcpy(nq->next,q->next,sizeof(q->next)); nq->par=q->par; q->par=nq; for(;p&&p->next[w]==q;p=p->par) p->next[w]=nq; last=nq; } else last=q; return; } Sam *np=newnode(p->step+1); for(;p&&!p->next[w];p=p->par) p->next[w]=np; if(!p) np->par=root; else { Sam *q=p->next[w]; if(q->step==p->step+1) np->par=q; else { Sam *nq=newnode(p->step+1); memcpy(nq->next,q->next,sizeof(q->next)); nq->par=q->par; q->par=np->par=nq; for(;p&&p->next[w]==q;p=p->par) p->next[w]=nq; } } last=np; } int c[maxn]; Sam *sa[2*maxn]; void csort() { For(i,1,tot-1) c[(pool+i)->step]++; For(i,1,len) c[i]+=c[i-1]; Rep(i,tot-1,1) sa[c[(pool+i)->step]--]=pool+i; For(i,1,tot-1) sa[i]->w=sa[i]->right>=K? sa[i]->step-sa[i]->par->step:0; For(i,1,tot-1) sa[i]->w+=sa[i]->par->w; } int main() { read(n); read(K); root=last=newnode(0); For(i,1,n) { scanf("%s",s+1); m=strlen(s+1); len=max(len,m); last=root; L[i]=t+1; For(j,1,m) { extend(s[j]-'a'+1); node[++t]=last; } R[i]=t; } For(i,1,n) For(j,L[i],R[i]) for(Sam *p=node[j];p!=root&&p->cur!=i;p=p->par) { p->cur=i; ++p->right; } csort(); For(i,1,n) { ans=0; For(j,L[i],R[i]) ans+=node[j]->w; printf("%lld ",ans); } printf("\n"); return 0; }
回文自动机
一开始像Sam一样写了一个全是指针的,后来调得难受就改了
题目大意:一开始你有一个空串,你每次可以在开头或者结尾加一个字母,也可以把当前整个串翻转后放在这个串后面,问得到S的最小步数
建一个回文自动机,顺便对每个点求一个trans,表示这个回文串的后缀回文串中只在这个串的右半部分的最长回文串,直接在回文自动机上面dp。
如果用x更新R[x].next[i](y),那么y用不是它后缀的回文串更新的情况已经包含在x更新它的情况里面了,所以非常优秀
考虑各种情况证明,真是太麻烦了,和Achen证了很久。
update20180310:注意在字符串转换为数字的时候,不要出现0,就是说extend的w不能为0,否则会出问题
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=1e5+7; int Td,n,a[maxn],ans; char s[maxn]; char cc; ll ff; template<typename T>void read(T& aa) { aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } struct Pam{ int trans,fail,next[7]; int len,f; Pam(){} Pam(int len):len(len){} }R[maxn]; int tot,len,last; int newnode(int len) { Pam *o=R+(tot++); o->len=len; o->f=0; o->fail=0; o->trans=0; memset(o->next,0,sizeof(o->next)); return o-R; } void extend(int w) { ++len; while(w!=a[len-R[last].len-1]) last=R[last].fail; if(!R[last].next[w]) { int np=newnode(R[last].len+2); int p=R[last].fail; while(w!=a[len-R[p].len-1]) p=R[p].fail; R[np].fail=R[p].next[w]; if(R[np].len<=2) R[np].trans=R[np].fail; else { int q=R[last].trans; while(w!=a[len-R[q].len-1]||(R[q].len+2)*2>R[np].len) q=R[q].fail; R[np].trans=R[q].next[w]; } R[last].next[w]=np; } last=R[last].next[w]; } int zz[maxn]; void solve() { For(i,2,tot-1) if(R[i].len&1) R[i].f=R[i].len; R[0].f=1; int s=1,t=0; int x,y; zz[++t]=0; while(s<=t) { x=zz[s++]; For(i,1,4) if(R[x].next[i]) { zz[++t]=y=R[x].next[i]; R[y].f=R[x].f+1; R[y].f=min(R[y].f,R[y].len/2-(R[R[y].trans].len)+(R[R[y].trans].f)+1); ans=min(ans,n-R[y].len+R[y].f); } } } int main() { read(Td); while(Td--) { scanf("%s",s+1); n=strlen(s+1); tot=0;len=0; ans=n; last=0; newnode(0); newnode(-1); R[0].fail=R[1].fail=1; For(i,1,n) { a[i]= s[i]=='A'? 1:(s[i]=='T'? 2:(s[i]=='C'? 3:4)); extend(a[i]); } solve(); printf("%d\n",ans); } return 0; }
update20180503
放一个现在的板子,题目是https://vjudge.net/problem/588757
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=2e5+7,W=27; const ll mod=1e9+7; int n,a[maxn]; char s[maxn]; ll ans; char cc; ll ff; template<typename T>void read(T& aa) { aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } struct Pam{ int step,next[W],fail;ll sum; }pm[maxn]; int tot,len,last; int newnode(int step) { pm[tot].step=step; pm[tot].fail=0; pm[tot].sum=0; memset(pm[tot].next,0,sizeof(pm[tot].next)); return tot++; } void extend(int w) { a[++len]=w; while(w!=a[len-pm[last].step-1]) last=pm[last].fail; if(!pm[last].next[w]) { int np=newnode(pm[last].step+2),p=pm[last].fail; while(w!=a[len-pm[p].step-1]) p=pm[p].fail; pm[np].fail=pm[p].next[w]; pm[last].next[w]=np; } last=pm[last].next[w]; ++pm[last].sum; } int main() { scanf("%s",s+1); n=strlen(s+1); newnode(0); newnode(-1); pm[0].fail=1; For(i,1,n) extend(s[i]-'a'+1); Rep(i,tot-1,2) pm[pm[i].fail].sum+=pm[i].sum; ll x; For(i,2,tot-1) { x=pm[i].sum; ans+=x*(x-1)/2; } printf("%lld\n",ans%mod); return 0; }
bzoj4199 品酒大会
后缀数组+并查集合并
一开始没考虑先把答案全弄成-INF。
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=6e5+7; const ll INF=1e18; int n; ll now1=0,now2=-INF,maxnum[maxn],minnum[maxn],ans1[maxn],ans2[maxn],sum[maxn]; char s[maxn]; char cc; ll ff; template<typename T>void read(T& aa) { aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int f[maxn]; int find(int x) {return x==f[x]? x:f[x]=find(f[x]);} int c[maxn],sa[maxn],x[maxn],y[maxn],h[maxn]; bool xd(int a,int b,int n,int k) { if(a+k>n||b+k>n) return 0; return y[a]==y[b]&&y[a+k]==y[b+k]; } struct Node{ ll x,num; Node(){} Node(ll x,ll num):x(x),num(num){} bool operator < (const Node& b) const{return num>b.num;} }node[maxn]; int tot; void get_sa(int n,int m) { int p; For(i,1,m) c[i]=0; For(i,1,n) c[x[i]=s[i]]++; For(i,1,m) c[i]+=c[i-1]; Rep(i,n,1) sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { p=0; Rep(i,n,n-k+1) y[++p]=i; For(i,1,n) if(sa[i]>k) y[++p]=sa[i]-k; For(i,1,m) c[i]=0; For(i,1,n) c[x[i]]++; For(i,1,m) c[i]+=c[i-1]; Rep(i,n,1) sa[c[x[y[i]]]--]=y[i]; swap(x,y); p=1; x[sa[1]]=1; For(i,2,n) x[sa[i]]=xd(sa[i],sa[i-1],n,k)? p:++p; if(p>=n) break; m=p; } For(i,1,n) x[sa[i]]=i; p=0; int u; For(i,1,n) if(x[i]!=1){ u=sa[x[i]-1]; if(p) p--; while(s[u+p]==s[i+p]) ++p; h[x[i]-1]=p; node[++tot]=Node(x[i]-1,p); } } ll F(ll t) { return t*(t-1)/2; } void lk(int x,int y) { x=find(x); y=find(y); now1-=F(sum[x])+F(sum[y]); now1+=F(sum[x]+sum[y]); now2=max(now2,maxnum[x]*maxnum[y]); now2=max(now2,minnum[x]*minnum[y]); f[y]=x; sum[x]+=sum[y]; maxnum[x]=max(maxnum[x],maxnum[y]); minnum[x]=min(minnum[x],minnum[y]); } int main() { read(n); scanf("%s",s+1); get_sa(n,'z'+1); For(i,1,n) { read(maxnum[x[i]]); minnum[x[i]]=maxnum[x[i]]; sum[x[i]]=1; f[i]=i; } sort(node+1,node+tot+1); For(i,0,n) ans2[i]=-INF; For(i,1,tot) { lk(node[i].x,node[i].x+1); if(i==tot||node[i+1].num<node[i].num) { ans1[node[i].num]=now1; ans2[node[i].num]=now2; } } Rep(i,n-1,0) ans1[i]=max(ans1[i],ans1[i+1]),ans2[i]=max(ans2[i],ans2[i+1]); For(i,0,n-1) printf("%lld %lld\n",ans1[i],ans1[i]? ans2[i]:0); return 0; }
bzoj4310 跳蚤
后缀数组+二分
一开始推了个假结论,以为答案一定是字典序最大的后缀的一个前缀。
然后一直wa,才发现自己太naive了。
随便造一个数据嘛
2
babd
答案明显是bab
所以说我们要在所有的本质不同的子串中二分。
然后一些区间一定有一个要切开的位置
把这些区间排序然后贪心就可以了
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=2e5+7; int n,k; char s[maxn]; char cc; ll ff; template<typename T>void read(T& aa) { aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int c[maxn],x[maxn],y[maxn],sa[maxn],h[maxn]; bool xd(int a,int b,int n,int k) { if(a+k>n||b+k>n) return 0; return y[a]==y[b]&&y[a+k]==y[b+k]; } void get_sa(int n,int m) { int p; For(i,1,m) c[i]=0; For(i,1,n) c[x[i]=s[i]]++; For(i,1,m) c[i]+=c[i-1]; Rep(i,n,1) sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { p=0; Rep(i,n,n-k+1) y[++p]=i; For(i,1,n) if(sa[i]>k) y[++p]=sa[i]-k; For(i,1,m) c[i]=0; For(i,1,n) c[x[i]]++; For(i,1,m) c[i]+=c[i-1]; Rep(i,n,1) sa[c[x[y[i]]]--]=y[i]; swap(x,y); p=1; x[sa[1]]=1; For(i,2,n) x[sa[i]]=xd(sa[i],sa[i-1],n,k)? p:++p; if(p>=n) break; m=p; } p=0; int u; For(i,1,n) if(x[i]!=1) { u=sa[x[i]-1]; if(p) --p; while(s[i+p]==s[u+p]) ++p; h[x[i]-1]=p; } } struct Node{ ll l,r; Node(){} Node(ll l,ll r):l(l),r(r){} bool operator < (const Node& b) const{return r<b.r;} }node[maxn]; int tot; void find(ll &pos,ll &len,ll x) { for(pos=1;pos<=n;++pos) { if(x<=n-sa[pos]+1-h[pos-1]) break; x-=(n-sa[pos]+1-h[pos-1]); } len=x+h[pos-1]; } void debug(ll pos,ll len) { printf("debug:"); For(i,sa[pos],sa[pos]+len-1) printf("%c",s[i]); printf("\n"); } bool check(ll x) { ll pos,len; find(pos,len,x); if(s[sa[pos]]!=s[sa[n]]) return 0; // debug(pos,len); node[tot=1]=Node(sa[pos],sa[pos]+len-1); Rep(i,pos-1,1) { if(h[i]<len) break; node[++tot]=Node(sa[i],sa[i]+len-1); } For(i,pos+1,n) { if(h[i-1]<len) len=h[i-1]; node[++tot]=Node(sa[i],sa[i]+len-1); } sort(node+1,node+tot+1); int now=0,sum=0; For(i,1,tot) { if(now<node[i].l) { now=node[i].r; ++sum; } } if(now!=n) ++sum; return sum<=k; } ll get_r() { ll rs=0; For(i,1,n) rs+=(n-sa[i]+1)-h[i]; return rs; } ll solve() { ll l=1,r=get_r(),mid; if(check(1)) return 1; while(l<r-1) { mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid; } return r; } int main() { read(k); scanf("%s",s+1); n=strlen(s+1); get_sa(n,200); ll pos=solve(),len; find(pos,len,pos); For(i,sa[pos],sa[pos]+len-1) printf("%c",s[i]); return 0; }
HDU 4622
Sam模板题,但是注意不能nQ做,会被卡,直接n^2预处理就可以了
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=2e3+7; int Td,n,m,ans[maxn][maxn],now; char s[maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } struct Sam{ Sam *next[27],*par; int step; }pool[2*maxn],*last,*root; int tot; Sam *newnode(int step) { Sam *o=pool+(tot++); o->step=step; o->par=NULL; memset(o->next,0,sizeof(o->next)); return o; } void extend(int w) { Sam *p=last; Sam *np=newnode(p->step+1); for(;p&&!p->next[w];p=p->par) p->next[w]=np; if(!p) np->par=root; else { Sam *q=p->next[w]; if(q->step==p->step+1) np->par=q; else { Sam *nq=newnode(p->step+1); memcpy(nq->next,q->next,sizeof(q->next)); nq->par=q->par; q->par=np->par=nq; for(;p&&p->next[w]==q;p=p->par) p->next[w]=nq; } } now+=np->step-np->par->step; last=np; } void clear() { tot=now=0; last=root=newnode(0); } int main() { read(Td); int l,r; while(Td--) { scanf("%s",s+1); n=strlen(s+1); read(m); For(i,1,n) { clear(); For(j,i,n) { extend(s[j]-'a'); ans[i][j]=now; } } For(i,1,m) { read(l); read(r); printf("%d\n",ans[l][r]); } } return 0; } /* 2 bbaba 5 3 4 2 2 2 5 2 4 1 4 baaba 5 3 3 3 4 1 4 3 5 5 5 */
UVA11019 Matrix Matcher
二维字符串匹配
n,m<=1000 X,Y<=100
晚上开始困,无法思考了,一开始写的时候把i写成len,后面改了之后,又交错了代码,感觉自己真是佩服自己呀
此题udebug真的很好用:https://www.udebug.com/UVa/11019
按照套路,往什么AC自动机、kmp上面靠。
然后发现,如果我们把小的(X*Y的)字符矩阵,没一行拆出来建AC自动机,我们就可以直接拿大的字符矩阵每行上去跑了。
因为AC自动机里的串是等长的(Y),所以串与串之间不会包含,只可能相等或不相等,所以不用跳fail,就非常优秀了。
但是AC自动机里面可能有相同的串,可以用vector一类的放一下(我喜欢set不行么)
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<set> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) const int maxn=1e5+7,maxl=1000+7,maxt=29,W=25; int Td,n,m,X,Y,ans[maxl][maxl],totans; char s[maxl],t[maxl][maxl]; char cc; ll ff; template<typename T>void read(T& aa) { aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int fail[maxn],son[maxn][maxt],tot; set<int> id[maxn]; set<int>::iterator it; void get_add(int p) { scanf("%s",s+1); int len=strlen(s+1),pos=0,x; For(i,1,len) { x=s[i]-'a'; if(!son[pos][x]) son[pos][x]=++tot; pos=son[pos][x]; } id[pos].insert(p); } int zz[maxn]; void bld() { int s=1,t=0,x; For(i,0,W) if(son[0][i]) zz[++t]=son[0][i]; while(s<=t) { x=zz[s++]; For(i,0,W) { if(!son[x][i]) son[x][i]=son[fail[x]][i]; else fail[son[x][i]]=son[fail[x]][i],zz[++t]=son[x][i]; } } } void get_ans(int p,char* s) { int len=strlen(s+1),pos=0,x; For(i,1,len) { x=s[i]-'a'; pos=son[pos][x]; for(it=id[pos].begin();it!=id[pos].end();++it) if(p>=*it) ++ans[p-*it+1][i-Y+1]; } } void clear() { For(i,0,tot) memset(son[i],0,sizeof(son[i])); For(i,0,tot) fail[i]=0,id[i].clear(); For(i,1,n) For(j,1,m) ans[i][j]=0; tot=totans=0; } int main() { read(Td); while(Td--) { read(n); read(m); clear(); For(i,1,n) scanf("%s",t[i]+1); read(X); read(Y); For(i,1,X) get_add(i); bld(); For(i,1,n) get_ans(i,t[i]); For(i,1,n) For(j,1,m) if(ans[i][j]==X) totans++; printf("%d\n",totans); } return 0; }