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

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

题目链接:luogu P6139

题目大意

给你 n 个串,问你这里一共有多少个本质不同的子串。

思路

其实这道题我们只需要每次插入一个串之后插下一个串之间回到起点就可以了。

所以 SAM 还是普通的 SAM,求个数也是直接 \(len_i-len_{fa_i}\),没有什么太多少说的。

代码

#include<cstdio>
#include<cstring>
#define ll long long

using namespace std;

struct SAM {
	int len, fa;
	ll size;
	int son[26];
}t[2000002];
int n, sn, tot, lst;
char s[1000001];
ll ans;

void SAM_insert(int now) {
	int p = lst;
	int np = ++tot;
	lst = np;
	t[np].size = 1;
	t[np].len = t[p].len + 1;
	for (; p && !t[p].son[now]; p = t[p].fa)
		t[p].son[now] = np;
	
	if (!p) t[np].fa = 1;
		else {
			int q = t[p].son[now];
			if (t[q].len == t[p].len + 1) t[np].fa = q;
				else {
					int nq = ++tot;
					t[nq] = t[q];
					t[nq].size = 0;
					t[nq].len = t[p].len + 1;
					t[q].fa = t[np].fa = nq;
					for (; p && t[p].son[now] == q; p = t[p].fa)
						t[p].son[now] = nq;
				}
		}
}

int main() {
	scanf("%d", &n);
	
	tot = 1;
	for (int i = 1; i <= n; i++) {
		scanf("%s", s + 1);
		sn = strlen(s + 1);
		
		lst = 1;//每次重新回起点搞
		for (int j = 1; j <= sn; j++)
			SAM_insert(s[j] - 'a');
	}
	
	for (int i = 1; i <= tot; i++)
		ans += t[i].len - t[t[i].fa].len;
	printf("%lld", ans);
	
	return 0;
}
posted @ 2021-08-24 09:27  あおいSakura  阅读(23)  评论(0编辑  收藏  举报