【BZOJ1396/2865】识别子串(SAM)(线段树)

传送门


题解:

建出SAM,能够用来更新答案的是right集合大小为1的,也就是叶子节点,也就是原串的每个位置的代表节点。

考虑节点iii,显然[len[fa[i]]+1,len[i]][len[fa[i]]+1,len[i]][len[fa[i]]+1,len[i]]这段区间可以直接被len[fa[i]]+1len[fa[i]]+1len[fa[i]]+1更新。

然后对于[1,len[i]len[fa[i]]1][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;
}
上一篇:Java调用C/C++编写的第三方dll动态链接库(zz)


下一篇:sam格式