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
", ans);
}
}