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

题目

题目链接:https://www.luogu.com.cn/problem/P6139
给定 \(n\) 个由小写字母组成的字符串 \(s_1,s_2\ldots s_n\),求本质不同的子串个数。(不包含空串)
\(\sum |S|\leq 10^6\)

思路

广义 SAM 直接在 SAM 的基础上加上特判即可。具体的,每次插入完一个字符串后 \(last\) 要赋值回 \(1\),再次插入时可能会遇到之前已经插入过的串,这个时候特判一下不要新建节点即可。
时间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=2000010;
int Q,n,a[N],b[N];
char s[N];

struct SAM
{
	int tot,last,ch[N][26],len[N],fa[N];
	ll siz[N];
	SAM() { tot=last=1; }
	
	void ins(int c)
	{
		int p=last;
		if (ch[p][c])
		{
			int q=ch[p][c];
			if (len[q]==len[p]+1) last=q;
			else
			{
				int nq=++tot; 
				len[nq]=len[p]+1; fa[nq]=fa[q]; last=nq;
				for (int i=0;i<26;i++) ch[nq][i]=ch[q][i];
				fa[q]=nq;
				for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
			}
		}
		else
		{
			int np=++tot;
			len[np]=len[p]+1; last=np;
			for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
			if (!p) fa[np]=1;
			else
			{
				int q=ch[p][c];
				if (len[q]==len[p]+1) fa[np]=q;
				else
				{
					int nq=++tot;
					len[nq]=len[p]+1; fa[nq]=fa[q];
					for (int i=0;i<26;i++) ch[nq][i]=ch[q][i];
					fa[q]=fa[np]=nq;
					for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
				}
			}
		}
	}
	
	ll dfs(int x)
	{
		if (siz[x]) return siz[x];
		siz[x]=(x>1);
		for (int i=0;i<26;i++)
			if (ch[x][i]) siz[x]+=dfs(ch[x][i]);
		return siz[x];
	}
}sam;

int main()
{
	scanf("%d",&Q);
	while (Q--)
	{
		scanf("%s",s+1);
		n=strlen(s+1);
		sam.last=1;
		for (int i=1;i<=n;i++)
			sam.ins(s[i]-'a');
	}
	printf("%lld",sam.dfs(1));
	return 0;
}
posted @ 2021-01-13 16:05  stoorz  阅读(98)  评论(0编辑  收藏  举报