模板:广义SAM(字符串)

34 篇文章 0 订阅
8 篇文章 0 订阅

所谓广义SAM,就是更广泛意义下的SAM

(逃)

前言

感觉字符串的理解难度的巅峰还是在SAM,广义SAM只是在套一些特判罢了,并不是太难理解。
可以解决多字符串的子串问题,几乎就是把SAM能做的东西从单串变成了多串。

解析

在线做法

有一个很流行的盗版做法是每次加入新串时把 l s t lst lst 重新赋值为 1 1 1,然后就和正常SAM一样做。
但这会出现一些问题,就是当 t r a n s l s t , c trans_{lst,c} translst,c 存在时,我们会加入空节点
这里的空节点的具体含义是指其 m i n l e n > m a x l e n minlen>maxlen minlen>maxlen,从而使得该结点代表的等价类没有任何字符串。
这个时候我们进行特判即可,后面还是按照正常SAM做。

struct SAM{
	int tr[N][26],fa[N],len[N],tot,lst;
	SAM(){tot=lst=1;}
	
	inline int ins(int c,int p){
		c-='a';
		if(tr[p][c]){
			int q=tr[p][c];
			if(len[q]==len[p]+1) return q;
			else{
				int nq=++tot;
				len[nq]=len[p]+1;fa[nq]=fa[q];
				for(int i=0;i<26;i++) tr[nq][i]=tr[q][i];
				fa[q]=nq;
				for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
				return nq;
			}
		}
		else{
			int cur=++tot;len[cur]=len[p]+1;
			for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=cur;
			if(!tr[p][c]) fa[cur]=1;
			else{
				int q=tr[p][c];
				if(len[q]==len[p]+1) fa[cur]=q;
				else{
					int nq=++tot;
					len[nq]=len[p]+1;fa[nq]=fa[q];
					for(int i=0;i<26;i++) tr[nq][i]=tr[q][i];
					fa[q]=fa[cur]=nq;
					for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
				}
			}
			return cur;
		}
	}
	inline void addstring(char *s){
		int n=strlen(s+1),lst=1;
		for(int i=1;i<=n;i++) lst=ins(s[i],lst);
		return;
	}
}sam;

然而,这个做法对一棵 t r i e trie trie 树建SAM的时候复杂度是 O ( G ( T ) ) O(G(T)) O(G(T)) 的( G ( T ) G(T) G(T) 表示所有叶子的深度和),这在许多时候会被卡成 O ( n 2 ) O(n^2) O(n2),因此,我们需要寻找另一种做法。

离线做法

这个方法对于 t r i e trie trie 树建SAM的复杂度是优秀的 O ( T ) O(T) O(T)
考虑bfs,从而使得深度单调不降。
每次取出队首,以它父亲在SAM上对应的结点 作为 l s t lst lst 进行插入即可。由于 t r i e trie trie 树本身的性质,此时必然有 t r a n s l s t , c = N U L L trans_{lst,c}=NULL translst,c=NULL,因此直接按照正常的SAM写即可。

struct SAM{
	int tr[N][26],pos[N],len[N],fa[N],tot;
	
	SAM(){tot=1;}
	
	inline int ins(int c,int p){
		int cur=++tot;
		len[cur]=len[p]+1;
		for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=cur;
		if(!tr[p][c]) fa[cur]=1;
		else{
			int q=tr[p][c];
			if(len[q]==len[p]+1) fa[cur]=q;
			else{
				int nq=++tot;
				len[nq]=len[p]+1;fa[nq]=fa[q];
				for(int i=0;i<26;i++) tr[nq][i]=tr[q][i];
				fa[q]=fa[cur]=nq;
				for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
			}
		}
		return cur;
	}
	
	int q[N],st,ed;
	void build(){
		q[st=ed=1]=1;
		pos[1]=1;
		while(st<=ed){
			int now=q[st++];
			//pos[now]=ins(trie.col[now],pos[trie.fa[now]]);
			for(int i=0;i<26;i++){
				int to=trie.tr[now][i];
				if(to){
					q[++ed]=to;
					pos[to]=ins(trie.col[to],pos[now]);
				}
			}
		}
		return;
	}
}sam;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值