【BZOJ1396/2865】识别子串(SAM)(线段树)
作者:互联网
传送门
题解:
建出SAM,能够用来更新答案的是right集合大小为1的,也就是叶子节点,也就是原串的每个位置的代表节点。
考虑节点i,显然[len[fa[i]]+1,len[i]]这段区间可以直接被len[fa[i]]+1更新。
然后对于[1,len[i]−len[fa[i]]−1],可以用一个等差数列更新,其实就是它右边离他最近的端点,用两个线段树维护一下上面两个更新就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
cs int N=2e5+15;
char s[N];int n;
int son[N][26],fa[N],len[N],siz[N],bin[N],nd[N],now;
inline void push_back(int c,int i){
int cur=i,p=(i-1)?(i-1):(n+1);
len[cur]=len[p]+1;siz[cur]=1;
for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
if(!p)fa[cur]=n+1;
else if(len[son[p][c]]==len[p]+1)fa[cur]=son[p][c];
else {
int nq=++now,q=son[p][c];
memcpy(son[nq],son[q],sizeof son[q]);
len[nq]=len[p]+1;fa[nq]=fa[q];
fa[q]=fa[cur]=nq;
for(;p&&son[p][c]==q;p=fa[p])son[p][c]=nq;
}
}
struct SGT{
#define lc u<<1
#define rc u<<1|1
int val[N<<1],mn[N];
SGT(){memset(val,0x3f,sizeof val);}
inline void modify(int u,int l,int r,int ql,int qr,int vl){
if(ql<=l&&r<=qr){val[u]=std::min(val[u],vl);return ;}
int mid=l+r>>1;
if(ql<=mid)modify(lc,l,mid,ql,qr,vl);
if(mid<qr)modify(rc,mid+1,r,ql,qr,vl);
}
inline void pushall(int u,int l,int r,int Mn=0x3f3f3f3f){
Mn=std::min(Mn,val[u]);
if(l==r){mn[l]=Mn;return ;}
int mid=l+r>>1;
pushall(lc,l,mid,Mn);pushall(rc,mid+1,r,Mn);
}
#undef lc
#undef rc
}a,b;
signed main(){
#ifdef zxyoi
freopen("string.in","r",stdin);
#endif
scanf("%s",s+1);n=strlen(s+1);now=n+1;
for(int re i=1;i<=n;++i)push_back(s[i]-'a',i);
for(int re i=1;i<=now;++i)++bin[len[i]];
for(int re i=1;i<=n;++i)bin[i]+=bin[i-1];
for(int re i=1;i<=now;++i)nd[bin[len[i]]--]=i;
for(int re i=now;i;--i)siz[fa[nd[i]]]+=siz[nd[i]];
for(int re i=1;i<=n;++i){
if(siz[i]>1)continue;
a.modify(1,1,n,i-len[fa[i]],i,len[fa[i]]+1);
if(i-len[fa[i]]>1)b.modify(1,1,n,1,i-len[fa[i]]-1,i+1);
}
a.pushall(1,1,n);b.pushall(1,1,n);
for(int re i=1;i<=n;++i)std::cout<<std::min(a.mn[i],b.mn[i]-i)<<"\n";
return 0;
}
标签:2865,SAM,int,len,son,fa,nq,BZOJ1396,cur 来源: https://blog.csdn.net/zxyoi_dreamer/article/details/101224344