2019.04.04【51nod1600】Simple KMP(SAM)(LCT)(差分)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/89010872

传送门


解析:

想法是算出每次新加一个字符的贡献增量,也就是差分数组。

我们发现每次新加入一个字符,会产生 l e n len 个新的后缀, l e n len 是加入前字符串的长度。

而在这些新的后缀的子串中,不是后缀的所有串的贡献其实就是之前作为过后缀的所有串的贡献,所以我们只需要算增量的差分数组,也就是二阶差分数组。

所以我们需要考虑每一个后缀在之前的所有位置出现了多少次。

显然我们直接提取最后插入节点到SAM根节点的fail链,(直接上LCT)。

我们发现每个节点表示的字符串出现次数实际上就是right集合大小,而每个节点代表的字符串个数就是 u > l e n u > f a > l e n u->len-u->fa->len ,直接上LCT维护答案就行了。

然后直接二阶差分回去就行了。

(实际上51nod数据水到家了,现在榜上rank1还是个 O ( n 2 ) O(n^2) 的暴力跳fail树,这位老哥还在讨论区怼出题人说数据过于水来着。。。)


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

using std::cout;
using std::cerr;
using std::cin;

cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(int a,int b){return (ll)a*b%mod;}

cs int N=2e5+5;

namespace LCT{
	static cs int N=::N<<1;
	int son[N][2],fa[N];
	int len[N],sumlen[N],val[N],r[N],add[N];
	
	inline bool isroot(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
	inline bool which(int u){return son[fa[u]][1]==u;}
	
	inline void pushup(int u){
		sumlen[u]=len[u];
		val[u]=mul(len[u],r[u]);
		if(son[u][0]){
			sumlen[u]=::add(sumlen[u],sumlen[son[u][0]]);
			val[u]=::add(val[u],val[son[u][0]]);
		}
		if(son[u][1]){
			sumlen[u]=::add(sumlen[u],sumlen[son[u][1]]);
			val[u]=::add(val[u],val[son[u][1]]);
		}
	}
	inline void pushadd(int u,int w){
		add[u]=::add(add[u],w);
		r[u]=::add(r[u],w);
		val[u]=::add(val[u],mul(w,sumlen[u]));
	}
	inline void pushdown(int u){
		if(add[u]){
			if(son[u][0])pushadd(son[u][0],add[u]);
			if(son[u][1])pushadd(son[u][1],add[u]);
			add[u]=0;
		}
	}
	
	inline void Rotate(int u){
		int Fa=fa[u],FA=fa[Fa];
		bool pos=which(u);
		if(FA&&!isroot(Fa))son[FA][which(Fa)]=u;
		son[Fa][pos]=son[u][!pos];
		if(son[u][!pos])fa[son[u][!pos]]=Fa;
		son[u][!pos]=Fa;
		fa[Fa]=u,fa[u]=FA;
		pushup(Fa),pushup(u);
	}
	
	inline void Splay(int u){
		static int q[N],qn;
		q[qn=1]=u;
		for(int re Fa=u;!isroot(Fa);Fa=fa[Fa])q[++qn]=fa[Fa];
		while(qn)pushdown(q[qn--]);
		for(int re Fa=fa[u];!isroot(u);Rotate(u),Fa=fa[u])
		if(!isroot(Fa))Rotate(which(Fa)==which(u)?Fa:u);
	}
	
	inline void access(int u){
		for(int re ch=0;u;u=fa[ch=u]){
			Splay(u);son[u][1]=ch;pushup(u);
		}
	}
	
	inline void select(int u){access(u);Splay(u);}
	
	inline void link(int u,int v){
		select(v);
		fa[u]=v;
		
	}
	
	inline void cut(int u,int v){
		select(u);
		Splay(v);
		son[v][1]=fa[u]=0;
		pushup(v);
	}
}

namespace SAM{
	static cs int N=::N<<1;
	int son[N][26],fa[N],len[N],r[N];
	int last=1,now=1;
	
	inline void init_LCT(int cur){
		LCT::len[cur]=len[cur]-len[fa[cur]];
		LCT::r[cur]=r[cur];
		LCT::pushup(cur);
	}
	
	inline int push_back(char c){
		c-='a';
		int cur=++now,p=last;
		len[cur]=len[last]+1,r[cur]=1;
		for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
		if(!p)fa[cur]=1,init_LCT(cur),LCT::link(cur,1);
		else if(len[son[p][c]]==len[p]+1)fa[cur]=son[p][c],init_LCT(cur),LCT::link(cur,fa[cur]);
		else {
			int clone=++now,q=son[p][c];
			len[clone]=len[p]+1;
			memcpy(son[clone],son[q],sizeof son[q]);
			fa[clone]=fa[q];
			LCT::select(q);
			r[clone]=r[q]=LCT::r[q];
			init_LCT(clone);
			LCT::link(clone,fa[q]);
			LCT::cut(q,fa[q]);
			fa[q]=fa[cur]=clone;
			init_LCT(q);
			init_LCT(cur);
			LCT::link(q,clone);
			LCT::link(cur,clone);
			for(;p&&son[p][c]==q;p=fa[p])son[p][c]=clone;
		}
		last=cur;
		LCT::select(cur);
		int now=LCT::son[cur][0];
		int res=LCT::val[now];
		LCT::pushadd(now,1);
		return res;
	}
}

int n;
char s[N];
int res,ans;
signed main(){
	std::ios::sync_with_stdio(false);
	cin>>n>>(s+1);
	for(int re i=1;i<=n;++i){
		res=add(res,SAM::push_back(s[i]));
		cout<<(ans=add(ans,res))<<"\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/89010872