P6139 【模板】广义后缀自动机(广义 SAM)

研究了下概念,就学会了基于字典树的广义SAM构造方法。

北大带哥提出的别的高效构造完全不会了...

SAM是基于字符串的,GSAM就是把lst换成字典树的上一个节点就行了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e6+100;
int ch[maxn][26];
int ed[maxn];//每个字典树节点在GSAM里的节点编号,记得初始化ed[0]=1 
int tol;
int len[maxn],link[maxn],nxt[maxn][26];
int sz[maxn],tot=1,lst=1;
void insert (string s) {
	int u=0;
	for (char i:s) {
		if (!ch[u][i-'a']) ch[u][i-'a']=++tol;
		u=ch[u][i-'a'];
	}
}
int sam_extend (char c,int lst) {
	int cur=++tot;
	len[cur]=len[lst]+1;
	sz[cur]=1;
	int p=lst;
	while (p&&!nxt[p][c-'a']) {
		nxt[p][c-'a']=cur;
		p=link[p];
	}
	if (!p) {
		link[cur]=1;
	}
	else {
		int q=nxt[p][c-'a'];
		if (len[p]+1==len[q]) {
			link[cur]=q;
		}
		else {
			int clone=++tot;
			len[clone]=len[p]+1;
			for (int i=0;i<26;i++) {
				nxt[clone][i]=nxt[q][i];
			}
			link[clone]=link[q];
			while (p&&nxt[p][c-'a']==q) {
				nxt[p][c-'a']=clone;
				p=link[p];
			}
			link[q]=link[cur]=clone;
		}
	}
	sz[cur]=1;
	return cur;
}
void GSA (int u,int f,char lc) {
	if (u) {
		ed[u]=sam_extend(lc,ed[f]);
	} 
	for (int i=0;i<26;i++) {
		if (ch[u][i]) {
			GSA(ch[u][i],u,i+'a');
		}
	}
}
int n;
int main () {
	scanf("%d",&n);
	for (int i=1;i<=n;i++) {
		string s;
		cin>>s;
		insert(s);
	}
	ed[0]=1;
	GSA(0,-1,'a');
	long long ans=0;
	for (int i=2;i<=tot;i++) {
		ans+=len[i]-len[link[i]];
	}
	printf("%lld\n",ans);
	
}
posted @ 2021-07-15 23:22  zlc0405  阅读(49)  评论(0编辑  收藏  举报