题目大意
化简一下题意!
现在给你一个长度为
N
的字符串,对于每个字符
N≤100000
解题思路
我们考虑先对原串建SAM,问题就变成了统计一个前缀所在节点的
fail
链上有多少
right
是小于
i
的,我们考虑对
程序
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 2e5 + 5;
const int Mo = 1e9 + 7;
struct SAM {
int fail, Len, Go[26];
} A[MAXN];
struct Tree {
int tag, Sum, Num;
} Tr[MAXN * 4];
char S[MAXN];
int N, Root, tot, Lst, time;
int Cnt, Last[MAXN], Go[MAXN * 2], Next[MAXN * 2];
int Size[MAXN], MSon[MAXN], Top[MAXN], Ord[MAXN], Bel[MAXN], Pre[MAXN];
void Link(int u, int v) {
Next[++ Cnt] = Last[u], Last[u] = Cnt, Go[Cnt] = v;
}
void Add(int ch) {
int np = ++ tot, p = Lst;
A[np].Len = A[p].Len + 1;
for (; p && !A[p].Go[ch]; p = A[p].fail) A[p].Go[ch] = np;
if (!p) A[np].fail = Root; else {
int q = A[p].Go[ch];
if (A[q].Len == A[p].Len + 1) A[np].fail = q; else {
int nq = ++ tot;
A[nq] = A[q];
A[nq].Len = A[p].Len + 1;
A[q].fail = A[np].fail = nq;
for (; p && A[p].Go[ch] == q; p = A[p].fail) A[p].Go[ch] = nq;
}
}
Lst = np;
}
void Basis(int Now) {
Size[Now] = 1;
for (int p = Last[Now]; p; p = Next[p]) {
int v = Go[p];
Pre[v] = Now;
Basis(v);
Size[Now] += Size[v];
if (Size[v] > Size[MSon[Now]]) MSon[Now] = v;
}
}
void Promote(int Now, int top) {
if (!Now) return;
Ord[Now] = ++ time;
Bel[time] = Now;
Top[Now] = top;
Promote(MSon[Now], top);
for (int p = Last[Now]; p; p = Next[p]) {
int v = Go[p];
if (v == MSon[Now]) continue;
Promote(v, v);
}
}
void Build(int Now, int l, int r) {
if (l == r) {
int p = Bel[l];
Tr[Now].Num = A[p].Len - A[A[p].fail].Len;
return;
}
int Mid = (l + r) >> 1;
Build(Now * 2, l, Mid), Build(Now * 2 + 1, Mid + 1, r);
Tr[Now].Num = (Tr[Now * 2].Num + Tr[Now * 2 + 1].Num) % Mo;
}
void Prepare() {
Lst = tot = Root = 1;
for (int i = 1; i <= N; i ++) Add(S[i] - 'a');
for (int i = 1; i <= tot; i ++)
Link(A[i].fail, i);
Basis(1);
Promote(1, 1);
Build(1, 1, tot);
}
void MakeTag(int Now, int tag) {
Tr[Now].tag = (Tr[Now].tag + tag) % Mo;
Tr[Now].Sum = (Tr[Now].Sum + tag * 1ll * Tr[Now].Num % Mo) % Mo;
}
void Push(int Now, int l, int r) {
if (l != r && Tr[Now].tag) {
MakeTag(Now * 2, Tr[Now].tag);
MakeTag(Now * 2 + 1, Tr[Now].tag);
}
Tr[Now].tag = 0;
}
int Query(int Now, int l, int r, int lx, int rx) {
if (rx < l || lx > r) return 0;
Push(Now, l, r);
if (l >= lx && r <= rx) return Tr[Now].Sum;
int Mid = (l + r) >> 1;
return (Query(Now * 2, l, Mid, lx, rx) + Query(Now * 2 + 1, Mid + 1, r, lx, rx)) % Mo;
}
void Modify(int Now, int l, int r, int lx, int rx) {
if (rx < l || lx > r) return;
if (l >= lx && r <= rx) {
MakeTag(Now, 1);
return;
}
int Mid = (l + r) >> 1;
Modify(Now * 2, l, Mid, lx, rx), Modify(Now * 2 + 1, Mid + 1, r, lx, rx);
Tr[Now].Sum = (Tr[Now * 2].Sum + Tr[Now * 2 + 1].Sum) % Mo;
}
void Solve() {
int Ans = 0, Now = 0, p = Root;
for (int i = 1; i <= N; i ++) {
p = A[p].Go[S[i] - 'a'];
for (int P = p; P; P = Pre[Top[P]])
Now = (Now + Query(1, 1, tot, Ord[Top[P]], Ord[P])) % Mo;
Ans = (Ans + Now) % Mo;
printf("%d\n", Ans);
for (int P = p; P; P = Pre[Top[P]])
Modify(1, 1, tot, Ord[Top[P]], Ord[P]);
}
}
int main() {
scanf("%d", &N);
scanf("%s", S + 1);
Prepare();
Solve();
}