题目实际上要求我们求从每个点出发的AA串的数量
考虑点i的答案,发现如果前缀i与前缀j(j<=i)的最长公共后缀>=i-j,那么i点出发向前就存在一个长度为i-j的AA串,题目即求对于每个前缀,有多少个在他之前的前缀满足条件
考虑后缀自动机,由于每个前缀都是后缀自动机parent树上的一点,即两个前缀的最长公共后缀为两个前缀点在parent树上的lca的len长度。考虑在lca处统计贡献。
于是每个点都建立一颗动态线段树,要统计子树之间相互的贡献,按照启发式合并合并线段树,即继承重儿子的动态线段树,暴力将轻儿子的线段树上的节点合并到重儿子,统计贡献,具体会用到区间加(lazy_tag)和区间查询,时间复杂度O(Tnlog^2)
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mm(a, b) memset(a, b, sizeof(a))
#define LL long long
using namespace std;
const int Maxn = 100010;
int Len_s = 0;
char s[Maxn], Rev[Maxn];
int Ans[Maxn], id[Maxn * 4], Root[Maxn * 4], Ansl[Maxn], Ansr[Maxn];
int Size[Maxn * 26], lc[Maxn * 26], rc[Maxn * 26], tag[Maxn * 26];
int Vec_tot, Sam_tot, Seg_tot, Head, Last_prefix;
vector<int> Tree[Maxn * 4], Tree_suf[Maxn];
struct node {
int ch[26];
int fa, Len;
} dot[Maxn * 2];
void Init() { Sam_tot = Head = Last_prefix = 1; }
void pushdown(int h, int l, int r) {
if (!h)
return;
if (!tag[h])
return;
if (l == r) {
Ans[l] += tag[h];
tag[h] = 0;
return;
}
if (lc[h])
tag[lc[h]] += tag[h];
if (rc[h])
tag[rc[h]] += tag[h];
tag[h] = 0;
}
void Insert(int &h, int l, int r, int loc) {
pushdown(h, l, r);
if (!h)
h = ++Seg_tot;
if (l == r) {
Size[h] = 1;
return;
}
int mid = l + r >> 1;
if (loc <= mid)
Insert(lc[h], l, mid, loc);
else
Insert(rc[h], mid + 1, r, loc);
Size[h] = Size[lc[h]] + Size[rc[h]];
}
int Query_size(int h, int l, int r, int s, int e) {
pushdown(h, l, r);
if (!h)
return 0;
if (l == s && r == e)
return Size[h];
int mid = l + r >> 1;
if (e <= mid)
return Query_size(lc[h], l, mid, s, e);
else if (s > mid)
return Query_size(rc[h], mid + 1, r, s, e);
else
return Query_size(lc[h], l, mid, s, mid) + Query_size(rc[h], mid + 1, r, mid + 1, e);
}
void Query_sum(int h, int l, int r, int loc) {
pushdown(h, l, r);
if (l == r)
return;
int mid = l + r >> 1;
if (loc <= mid)
Query_sum(lc[h], l, mid, loc);
else
Query_sum(rc[h], mid + 1, r, loc);
}
void Add(int h, int l, int r, int s, int e) {
if (!h)
return;
pushdown(h, l, r);
if (l == s && r == e) {
tag[h] = 1;
pushdown(h, l, r);
return;
}
int mid = l + r >> 1;
if (e <= mid)
Add(lc[h], l, mid, s, e);
else if (s > mid)
Add(rc[h], mid + 1, r, s, e);
else {
Add(lc[h], l, mid, s, mid);
Add(rc[h], mid + 1, r, mid + 1, e);
}
}
void Create_sam(int c) {
int Cur = ++Sam_tot;
dot[Cur].Len = dot[Last_prefix].Len + 1;
id[Cur] = ++Vec_tot, Tree_suf[Vec_tot].push_back(dot[Cur].Len);
Insert(Root[Cur], 1, Len_s, dot[Cur].Len);
int Tmp, To;
for (Tmp = Last_prefix; Tmp && !(To = dot[Tmp].ch[c]); Tmp = dot[Tmp].fa) {
dot[Tmp].ch[c] = Cur;
}
Last_prefix = Cur;
if (!Tmp)
dot[Cur].fa = Head;
else if (dot[Tmp].Len + 1 == dot[To].Len)
dot[Cur].fa = To;
else {
++Sam_tot;
dot[Sam_tot] = dot[To];
dot[Sam_tot].Len = dot[Tmp].Len + 1;
dot[To].fa = dot[Cur].fa = Sam_tot;
while (Tmp && dot[Tmp].ch[c] == To) {
dot[Tmp].ch[c] = Sam_tot;
Tmp = dot[Tmp].fa;
}
}
}
void Add_edge() {
for (int i = 1; i <= Sam_tot; ++i) {
if (dot[i].fa == 0)
continue;
Tree[dot[i].fa].push_back(i);
}
}
void Calc_tree(int h) {
if ((int)Tree[h].size() == 0)
return;
int Maxval = 0, Maxid = 0;
for (int i = 0; i < (int)Tree[h].size(); ++i) {
int To = Tree[h][i];
Calc_tree(To);
if ((int)Tree_suf[id[To]].size() > Maxval) {
Maxval = (int)Tree_suf[id[To]].size();
Maxid = To;
}
}
for (int i = 0; i < (int)Tree[h].size(); ++i) {
int To = Tree[h][i];
if (To == Maxid)
continue;
for (int j = 0; j < (int)Tree_suf[id[To]].size(); ++j) {
int Node = Tree_suf[id[To]][j], Tmp;
Query_sum(Root[To], 1, Len_s, Node);
if ((Tmp = max(Node - dot[h].Len, 1)) <= Node - 1) {
Ans[Node] += Query_size(Root[Maxid], 1, Len_s, Tmp, Node - 1);
}
if ((Tmp = min(Node + dot[h].Len, Len_s)) >= Node + 1) {
Add(Root[Maxid], 1, Len_s, Node + 1, Tmp);
}
}
for (int j = 0; j < (int)Tree_suf[id[To]].size(); ++j) {
int Node = Tree_suf[id[To]][j];
Insert(Root[Maxid], 1, Len_s, Node);
Tree_suf[id[Maxid]].push_back(Node);
}
}
//¿¼ÂDz»ÖÜËùÖÂ, »¹Óнڵ㱾ÉíΪǰ׺µÄÇé¿ö
for (int i = 0; i < (int)Tree_suf[id[h]].size(); ++i) {
int Node = Tree_suf[id[h]][i], Tmp;
if ((Tmp = max(Node - dot[h].Len, 1)) <= Node - 1) {
Ans[Node] += Query_size(Root[Maxid], 1, Len_s, Tmp, Node - 1);
}
if ((Tmp = min(Node + dot[h].Len, Len_s)) >= Node + 1) {
Add(Root[Maxid], 1, Len_s, Node + 1, Tmp);
}
Insert(Root[Maxid], 1, Len_s, Node);
Tree_suf[id[Maxid]].push_back(Node);
}
Root[h] = Root[Maxid];
id[h] = id[Maxid];
}
void Arrange() {
Init();
for (int i = 1; i <= Len_s; ++i) {
Create_sam(s[i] - 'a');
}
Add_edge();
Calc_tree(Head);
for (int i = 0; i < (int)Tree_suf[id[Head]].size(); ++i) {
int Node = Tree_suf[id[Head]][i];
Query_sum(Root[Head], 1, Len_s, Node);
}
}
void Clear() {
for (int i = 1; i <= Seg_tot; ++i) lc[i] = rc[i] = tag[i] = Size[i] = 0;
for (int i = 1; i <= Len_s; ++i) Ans[i] = 0;
for (int i = 1; i <= Vec_tot; ++i) Tree_suf[i].clear();
for (int i = 1; i <= Sam_tot; ++i) {
Tree[i].clear();
dot[i].fa = dot[i].Len = Root[i] = id[i] = 0;
for (int j = 0; j < 26; ++j) {
dot[i].ch[j] = 0;
}
}
Vec_tot = Sam_tot = Seg_tot = 0;
}
void file() {
freopen("bona.in", "r", stdin);
freopen("bona.out", "w", stdout);
}
int main() {
file();
int T;
scanf("%d", &T);
while (T--) {
scanf("%s", s + 1);
Len_s = strlen(s + 1);
Arrange();
for (int i = 1; i <= Len_s; ++i) Ansl[i] = Ans[i];
Clear();
for (int i = 1; i <= Len_s; ++i) Rev[i] = s[Len_s - i + 1];
for (int i = 1; i <= Len_s; ++i) s[i] = Rev[i];
Arrange();
for (int i = 1; i <= Len_s; ++i) Ansr[i] = Ans[i];
Clear();
LL Sum = 0;
for (int i = 1; i < Len_s; ++i) {
Sum += 1ll * Ansl[i] * Ansr[Len_s - i];
}
printf("%lld\n", Sum);
}
return 0;
}