题意:给出 n n n个字符串,求它们所有不同子串的数字和(取模)。
题解:后缀自动机
先将
n
n
n个字符串拼接,然后建立sam,跑拓扑。
这里可以在串之间添加无效字符,也可以将
l
a
s
t
last
last指向
r
o
o
t
root
root,学到了学到了。
在遍历的时候,遇到
t
o
p
s
a
m
[
i
]
topsam[i]
topsam[i]若为初始状态,则
j
j
j从1开始。
c
n
t
[
i
]
cnt[i]
cnt[i]:从初始状态到状态
i
i
i中的所有不含分隔符以及前导0的子串数量。
cnt[topsam[i]->next[j]->id] += cnt[topsam[i]->id];
s
u
m
[
i
]
sum[i]
sum[i]:从初始状态到状态
i
i
i的所有合法路径形成的数字之和。
sum[topsam[i]->next[j]->id] += sum[topsam[i]->id] * 10 + j * cnt[topsam[i]->id];
然后累加。
ans += sum[topsam[i]->id];
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#define ll long long
using namespace std;
const int CHAR = 26;
const int MAXN = 200020;
struct SAM_Node {
SAM_Node* fa, * next[CHAR];
int len;
int id, pos;
SAM_Node() {}
SAM_Node(int _len) {
fa = 0;
len = _len;
memset(next, 0, sizeof(next));
}
};
SAM_Node SAM_node[MAXN * 2], * SAM_root, * SAM_last, * topsam[MAXN * 2];
int SAM_size, topcnt[MAXN];
SAM_Node* newSAM_Node(int len) {
SAM_node[SAM_size] = SAM_Node(len);
SAM_node[SAM_size].id = SAM_size;
return &SAM_node[SAM_size++];
}
SAM_Node* newSAM_Node(SAM_Node* p) {
SAM_node[SAM_size] = *p;
SAM_node[SAM_size].id = SAM_size;
return &SAM_node[SAM_size++];
}
void SAM_init() {
SAM_size = 0;
SAM_root = SAM_last = newSAM_Node(0);
SAM_node[0].pos = 0;
}
void SAM_add(int x) { //len从1开始
SAM_Node* p = SAM_last, * np = newSAM_Node(p->len + 1);
//np->pos = len;
SAM_last = np;
for (; p && !p->next[x]; p = p->fa)
p->next[x] = np;
if (!p) {
np->fa = SAM_root;
return;
}
SAM_Node* q = p->next[x];
if (q->len == p->len + 1) {
np->fa = q;
return;
}
SAM_Node* nq = newSAM_Node(q);
nq->len = p->len + 1;
q->fa = np->fa = nq;
for (; p && p->next[x] == q; p = p->fa) p->next[x] = nq;
}
void SAM_build(char* s) {
SAM_init();
int len = strlen(s);
for (int i = 0; i < len; i++) SAM_add(s[i] - 'a');
}
void topo() {
memset(topcnt, 0, sizeof(topcnt));
for (int i = 0; i <= SAM_size; i++) topcnt[SAM_node[i].len]++;
for (int i = 1; i <= SAM_size; i++) topcnt[i] += topcnt[i - 1];
for (int i = 0; i <= SAM_size; i++) topsam[--topcnt[SAM_node[i].len]] = &SAM_node[i];
}
char s[MAXN];
int n, sum[MAXN], cnt[MAXN];
int main() {
while (scanf("%d", &n) != EOF) {
SAM_init();
for (int i = 1; i <= n; i++) {
scanf("%s", s);
int len = strlen(s);
for (int i = 0; i < len; i++) SAM_add(s[i] - '0');
SAM_last = SAM_root;
//SAM_add(10);
}
topo();
memset(sum, 0, sizeof(sum));
memset(cnt, 0, sizeof(cnt));
cnt[0] = 1;
int ans = 0;
for (int i = 0; i <= SAM_size; i++) {
for (int j = (topsam[i]->id ? 0 : 1); j <= 9; j++) {
SAM_Node* p = topsam[i]->next[j];
if (p) {
cnt[p->id] += cnt[topsam[i]->id];
cnt[p->id] %= 2012;
sum[p->id] += sum[topsam[i]->id] * 10 + j * cnt[topsam[i]->id];
sum[p->id] %= 2012;
}
}
ans += sum[topsam[i]->id];
ans %= 2012;
}
printf("%d\n", ans);
}
return 0;
}