先建好后缀自动机,然后答案就是$sum_{u in 状态集合}len[u]-len[link[u]]$。
为什么这样是对的?
每个状态所代表的字符串是没有交集的,所以我们只需求出每个状态有多少个子串。
其实在学习构建SAM的时候我们学过link的一个性质,就是len[link[x]]+1=min_len(x),且一个状态内的字符串按长度从小到大排序是连续的。
通过这个性质很容易得到上面的式子。
#include <bits/stdc++.h> using namespace std; const int N = 1000010; struct SAM{ int ch[26], link, len; }tr[N]; int lst = 1, tot = 1; int n; long long ans; char s[N]; void insert(int c) { int p = lst; int np = lst = ++tot; tr[np].len = tr[p].len + 1; while (p && !tr[p].ch[c]) tr[p].ch[c] = np, p = tr[p].link; if (!p) tr[np].link = 1; else { int q = tr[p].ch[c]; if (tr[q].len == tr[p].len + 1) tr[np].link = q; else { int nq = ++tot; tr[nq] = tr[q]; tr[nq].len = tr[p].len + 1; tr[q].link = tr[np].link = nq; while (p && tr[p].ch[c] == q) tr[p].ch[c] = nq, p = tr[p].link; } } } int main() { cin >> n; scanf("%s", s + 1); for (int i = 1; i <= n; i++) { insert(s[i] - 'a'); } for (int i = 2; i <= tot; i++) { ans += tr[i].len - tr[tr[i].link].len; } cout << ans; return 0; }