后缀自动机SAM与广义后缀自动机

后缀自动机SAM

参见sxTQX的博客

后缀自动机的性质

本质不同子串个数

实际上是后缀自动机的DAG上DP,求DAG的路径数

但是我们有不需要DP的做法

答案为

\[\sum_{i=2}^{tot}len[i]-len[fa[i]] \]

设这个节点的最短长度为\(minlen_u\),最长长度为\(len_u\),那么这个节点包含的不同子串数是\(maxlen-minlen+1\)

注意到\(minlen_u=maxlen_{fa[u]}+1\),就可以得到上面的结论了

其他的很多问题都可以在DAG上DP完成

求LCS

即为\(parent\)树上的\(LCA\)\(len\)

广义后缀自动机 广义SAM

离线版

将所有模式串插入Trie,对Trie进行bfs依次插入,插入过程与SAM基本相同,只不过要从Trie树上的父亲在SAM中对应的节点往后插入

namespace SAM{
	int ch[N][26],fa[N],len[N],tot=1;
	inline int insert(int last,int a){
		int cur=last,id=++tot;
		len[id]=len[cur]+1;
		for(;cur&&!ch[cur][a];cur=fa[cur])ch[cur][a]=id;
		if(!cur){fa[id]=1;return id;}
		int q=ch[cur][a];
		if(len[q]==len[cur]+1){fa[id]=q;return id;}
		int nq=++tot;fa[nq]=fa[q];fa[id]=fa[q]=nq;
		for(int i=0;i<26;++i)ch[nq][i]=ch[q][i];
		len[nq]=len[cur]+1;
		for(;cur&&ch[cur][a]==q;cur=fa[cur])ch[cur][a]=nq;
		return id;
	}
}
namespace Trie{
	int ch[N][26],rt=1,tot=1,fa[N];
	inline void insert(char *s){
		int n=strlen(s+1),a,cur=rt;
		for(int i=1;i<=n;++i){
			a=s[i]-'a';
			if(!ch[cur][a])ch[cur][a]=++tot,fa[tot]=cur;
			cur=ch[cur][a];
		}
	}
	queue<int> q;
	int pos[N];
	inline void build(){
		q.push(1);pos[1]=1;
		while(!q.empty()){
			int u=q.front();q.pop();
			for(int i=0;i<26;++i)
				if(ch[u][i]){
					q.push(ch[u][i]);
					pos[ch[u][i]]=SAM::insert(pos[u],i);
				}
		}
	}
}

在线版

咕咕咕

posted @ 2021-04-20 19:28  harryzhr  阅读(73)  评论(0编辑  收藏  举报