Loading

【18焦作网络赛H】SAM后缀自动机

H - String and Times

询问子串出现次数范围在 \([A,B]\) 内的子串个数

\(SAM\) 很简单做,毒瘤题目没有说单组数据多大,就给了个 \(\sum|S| \le 2e6\)

图中的 last 集合就是每次的 last

在求 endpos 的时候按照 len 排序就是拓扑序

然后按拓扑序加就可以

/*
 * @Author: zhl
 * @Date: 2020-11-26 13:30:44
 */
#include<bits/stdc++.h>
using namespace std;

const int N = 4e5 + 10;
int tot = 1, last = 1;
struct Node {
	int len, fa;
	int ch[26];
}tr[N];

char s[N];

int sum[N], tp[N], cnt[N];
void extend(int c) {
	int p = last, np = last = ++tot;
	cnt[last] = 1;
	tr[np].len = tr[p].len + 1;
	for (; p && !tr[p].ch[c]; p = tr[p].fa) tr[p].ch[c] = np;
	if (!p)tr[np].fa = 1;
	else {
		int q = tr[p].ch[c];
		if (tr[q].len == tr[p].len + 1) tr[np].fa = q;
		else {
			int nq = ++tot;
			tr[nq] = tr[q]; tr[nq].len = tr[p].len + 1;
			tr[q].fa = tr[np].fa = nq;
			for (; p and tr[p].ch[c] == q; p = tr[p].fa) tr[p].ch[c] = nq;
		}
	}
}


void topo() {
	for (int i = 1; i <= tr[last].len; i++)sum[i] = 0;
	for (int i = 1; i <= tot; i++) sum[tr[i].len]++;
	for (int i = 1; i <= tr[last].len; i++)sum[i] += sum[i - 1];
	for (int i = 1; i <= tot; i++)tp[sum[tr[i].len]--] = i;
}
void init() {
	last = tot = 1;
	memset(cnt, 0, sizeof cnt);
	memset(tr, 0, sizeof tr);
}

int main() {
	while (~scanf("%s", s)) {
		init();
		int a, b; scanf("%d%d", &a, &b);
		
		for (int i = 0; s[i]; i++) extend(s[i] - 'A');

		topo();
		long long ans = 0;
		for (int i = tot; i >= 1; i--) {
			int p = tp[i], fp = tr[p].fa;
			cnt[fp] += cnt[p];
			if (cnt[p] >= a and cnt[p] <= b) ans += tr[p].len - tr[fp].len;
		}
		printf("%lld\n", ans);
	}
}
posted @ 2020-11-26 19:18  —O0oO-  阅读(92)  评论(0编辑  收藏  举报