bzoj1396识别子串(SAM+线段树)
复习SAM板子啦!考前刷水有益身心健康当然这不是板子题/水题……
很容易发现只在i位置出现的串一定是个前缀串。那么对答案的贡献分成两部分:一部分是len[x]-fa~len[x]的这部分贡献会是r-l+1;剩下一部分1~len-fa-1这部分会和i~r构成答案,写两棵线段树即可。
然后就又是板子题了,两个板子(SAM+线段树)套起来。
#include<bits/stdc++.h> #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; const int N=2e5+7,inf=0x3f3f3f3f; int n,lst=1,rt=1,cnt=1,ch[N][26],len[N],fa[N],sz[N]; char s[N]; struct tree{ int mn[N<<2]; void init(){memset(mn,0x3f,sizeof mn);} void pushdown(int rt) { if(mn[rt]==inf)return; mn[rt<<1]=min(mn[rt<<1],mn[rt]),mn[rt<<1|1]=min(mn[rt<<1|1],mn[rt]); mn[rt]=inf; } void update(int L,int R,int v,int l,int r,int rt) { if(L<=l&&r<=R){mn[rt]=min(mn[rt],v);return;} int mid=l+r>>1;pushdown(rt); if(L<=mid)update(L,R,v,lson); if(R>mid)update(L,R,v,rson); } int query(int k,int l,int r,int rt) { if(l==r)return mn[rt]; int mid=l+r>>1;pushdown(rt); if(k<=mid)return query(k,lson); return query(k,rson); } }tr1,tr2; void build(int c) { int p=lst,np=++cnt; len[np]=len[p]+1,sz[np]=1; while(p&&!ch[p][c])ch[p][c]=np,p=fa[p]; if(!p)fa[np]=rt; else{ int q=ch[p][c]; if(len[p]+1==len[q])fa[np]=q; else{ int nq=++cnt; memcpy(ch[nq],ch[q],sizeof ch[q]); fa[nq]=fa[q],fa[np]=fa[q]=nq,len[nq]=len[p]+1; while(p&&ch[p][c]==q)ch[p][c]=nq,p=fa[p]; } } lst=np; } int main() { scanf("%s",s+1),n=strlen(s+1); for(int i=1;i<=n;i++)build(s[i]-'a'); for(int i=1;i<=cnt;i++)sz[i]=1; for(int i=1;i<=cnt;i++)sz[fa[i]]=0; tr1.init(),tr2.init(); for(int i=1;i<=cnt;i++) if(sz[i]==1) { int l=len[i]-len[fa[i]],r=len[i]; if(l>=2)tr1.update(1,l-1,r+1,1,n,1); tr2.update(l,r,r-l+1,1,n,1); } for(int i=1;i<=n;i++)printf("%d\n",min(tr1.query(i,1,n,1)-i,tr2.query(i,1,n,1))); }