【18焦作網路賽H】SAM字尾自動機
阿新 • • 發佈:2020-11-26
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); } }