BZOJ3499 : PA2009 Quasi-template
建立后缀树,用线段树合并求出每个节点子树内部最靠前和最靠后的后缀位置以及相邻后缀距离的最大值,同时求出每个子串能完整匹配的最长后缀的长度。
对于一个子串,如果其长度不小于相邻后缀距离的最大值,且最靠后的位置加上最长匹配的后缀长度不小于$n$,那么就说明可以从中间开始覆盖到尾部。
对串做KMP,求出每个前缀的最长border,也就是$nxt$数组。
对于一个子串,设其最早出现的位置为$[l,r]$,那么只要$nxt[r]\geq l-1$,就说明可以覆盖头部。
因为后缀树边经过压缩,所以不能直接枚举所有子串。
对于计数,可以离线之后扫描线树状数组处理。
对于长度最小且字典序最小的解,可以考虑用线段树维护区间内$nxt$的最大值,然后在线段树上二分。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; const int inf=1<<30,S=27,N=200010; int root,last,pos,need,remain,acnode,ace,aclen; int n,i,j,nxt[N],f[N<<1],q[N],cnt,first,minl=inf,ansl,ansr; char text[N],a[N];long long ans; struct node{int st,en,lk,son[S];int len(){return min(en,pos+1)-st;}}tree[N<<1]; struct E{int x,l,r;E(){}E(int _x,int _l,int _r){x=_x,l=_l,r=_r;}}e[N<<1]; inline bool cmp(int x,int y){return nxt[x]>nxt[y];} inline bool cmpe(const E&a,const E&b){return a.x>b.x;} namespace DS{ const int M=3800000; int tot,l[M],r[M],v[M],vl[M],vr[M],T[N<<1]; int build(int a,int b,int c){ int x=++tot; vl[x]=vr[x]=c; if(a==b)return tot; int mid=(a+b)>>1; if(c<=mid)l[x]=build(a,mid,c);else r[x]=build(mid+1,b,c); return x; } int merge(int x,int y,int a,int b){ if(!x||!y)return x+y; int mid=(a+b)>>1; l[x]=merge(l[x],l[y],a,mid); r[x]=merge(r[x],r[y],mid+1,b); vl[x]=l[x]?vl[l[x]]:vl[r[x]]; vr[x]=r[x]?vr[r[x]]:vr[l[x]]; v[x]=max(v[l[x]],v[r[x]]); if(l[x]&&r[x])v[x]=max(v[x],vl[r[x]]-vr[l[x]]); return x; } } namespace RangeQuery{ int v[524300],bit[N]; void build(int x,int a,int b){ if(a==b){ v[x]=nxt[a]; return; } int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); v[x]=max(v[x<<1],v[x<<1|1]); } void get(int x,int a,int b,int c,int d,int p){ if(first<inf||v[x]<p)return; if(a==b)first=a; int mid=(a+b)>>1; if(c<=mid)get(x<<1,a,mid,c,d,p); if(d>mid)get(x<<1|1,mid+1,b,c,d,p); } inline void add(int x){for(x++;x<=n;x+=x&-x)bit[x]++;} inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;} inline int sum(int l,int r){return ask(r+1)-ask(l);} } int new_node(int st,int en=inf){ node nd; nd.st=st;nd.en=en; for(int i=nd.lk=0;i<S;i++)nd.son[i]=0; tree[++last]=nd; return last; } char acedge(){return text[ace];} void addedge(int node){ if(need)tree[need].lk=node; need=node; } bool down(int node){ if(aclen>=tree[node].len())return ace+=tree[node].len(),aclen-=tree[node].len(),acnode=node,1; return 0; } void init(){ need=last=remain=ace=aclen=0; root=acnode=new_node(pos=-1,-1); } void extend(char c){ text[++pos]=c;need=0;remain++; while(remain){ if(!aclen)ace=pos; if(!tree[acnode].son[acedge()])tree[acnode].son[acedge()]=new_node(pos),addedge(acnode); else{ int nxt=tree[acnode].son[acedge()]; if(down(nxt))continue; if(text[tree[nxt].st+aclen]==c){aclen++;addedge(acnode);break;} int split=new_node(tree[nxt].st,tree[nxt].st+aclen); tree[acnode].son[acedge()]=split; tree[split].son[c]=new_node(pos); tree[nxt].st+=aclen; tree[split].son[text[tree[nxt].st]]=nxt; addedge(split); } remain--; if(acnode==root&&aclen)aclen--,ace=pos-remain+1; else acnode=tree[acnode].lk?tree[acnode].lk:root; } } void dfs(int x,int y,int sum){ sum+=tree[x].len(); if(sum&&min(tree[x].en,pos+1)==pos+1){ if(!tree[x].len())f[y]=max(f[y],sum); else f[x]=sum; } for(int i=0;i<S;i++){ int u=tree[x].son[i]; if(!u)continue; dfs(u,x,sum); } } inline void solve(int l,int r,int x,int y,int ml){ l=max(l,max(DS::v[DS::T[x]],n-ml-DS::vr[DS::T[x]])); if(l>r)return; x=DS::vl[DS::T[x]]; if(l+10>r){ for(int i=l;i<=r;i++)if(nxt[y+i-1]>=x){ ans++; if(i<minl)minl=i,ansl=y,ansr=y+i-1; } return; } first=inf; RangeQuery::get(1,0,n,y+l-1,y+r-1,x); if(first==inf)return; first-=y-1; if(first<minl)minl=first,ansl=y,ansr=y+first-1; e[cnt++]=E(x,y+l-1,y+r-1); } void dfs2(int x,int y,int sum){ int l=sum+1; sum+=tree[x].len(); if(sum&&min(tree[x].en,pos+1)==pos+1)DS::T[x]=DS::build(0,n,pos-sum+1); for(int i=0;i<S;i++){ int u=tree[x].son[i]; if(!u)continue; f[u]=max(f[u],f[x]); dfs2(u,x,sum); DS::T[x]=DS::merge(DS::T[x],DS::T[u],0,n); } if(l<=sum){ solve(l,sum-1,x,tree[x].st-l+1,f[y]); solve(sum,sum,x,tree[x].st-l+1,f[x]); } } int main(){ init(); scanf("%s",a); n=strlen(a); for(nxt[0]=j=-1,i=1;i<n;nxt[i++]=j){ while(~j&&a[j+1]!=a[i])j=nxt[j]; if(a[j+1]==a[i])j++; } for(i=0;i<n;i++)nxt[i]++,q[i]=i; RangeQuery::build(1,0,n); for(i=0;i<n;i++)extend(a[i]-'a');extend(26); pos--; dfs(root,0,0); dfs2(root,0,0); sort(q,q+n,cmp); if(cnt>1)sort(e,e+cnt,cmpe); for(i=j=0;i<cnt;i++){ while(j<n&&nxt[q[j]]>=e[i].x)RangeQuery::add(q[j++]); ans+=RangeQuery::sum(e[i].l,e[i].r); } printf("%lld\n",ans); for(i=ansl;i<=ansr;i++)putchar(a[i]); return 0; }