更好的阅读体验:Press Here
传送门 >ω<
题目大意:
给定 n n 个由 0−9 0 − 9 组成的数字串,问本质不同串的总和是多少
设 cnt[x] c n t [ x ] 为 x x 状态包含的子串数量
若状态 x,y x , y 有转移 ch[x][c]=y c h [ x ] [ c ] = y ,则 sum[y]=sum[x]∗10+cnt[x]∗c s u m [ y ] = s u m [ x ] ∗ 10 + c n t [ x ] ∗ c
然后求出 ∑状态数i=1sum[i] ∑ i = 1 状态数 s u m [ i ] 即可
显然有 cnt[x]=l[x]−l[fa[x]] c n t [ x ] = l [ x ] − l [ f a [ x ] ] ,配合拓扑排序求出 sum[x] s u m [ x ] 可以在 O(|S|) O ( | S | ) 的时间内完成
在 SAM 中插入多个串需要利用特殊字符将其隔开,防止出现本不应该出现的子串
考虑多个串之间用 ‘:’ 隔开(ASCII 刚好是 ‘0’ + 10 或者说 ‘9’ + 1,比较方便)
然后观察原来的转移方程:
但是发现现在的子串中有可能包含 ‘:’ ,显然每个状态含有 ‘:’ 的子串是不能算在 cnt[x] c n t [ x ] 里面的,所以现在的问题就是如何求出每个状态不带 ‘:’ 的子串数量
SAM 是一个天生的 DAG ,可以用动态规划的形式来求出每个点的 cnt c n t ,也就是不包括 ‘:’ 的子串数量
设 rt r t 状态的子串数量为 1 (空串),然后通过 ch[x][0−9] c h [ x ] [ 0 − 9 ] 进行转移
通过拓扑同时求出 cnt,sum c n t , s u m 即可
#include
using namespace std;
const int N = 2000010;
const int mod = 1000000007;
int read() {
int ans = 0 , flag = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') flag = -1; ch = getchar();}
while(ch <= '9' && ch >= '0') {ans = ans * 10 + ch - '0'; ch = getchar();}
return ans * flag;
}
struct SAM {
int ch[N << 1][11] , fa[N << 1] , l[N << 1] , sum[N << 1] , bac[N << 1] , T[N] , d[N << 1];
int cnt = 1 , last = 1;
void ins(int c) {
int x = last , nx = ++ cnt; last = nx;
l[nx] = l[x] + 1;
for(; x && !ch[x][c] ; x = fa[x]) ch[x][c] = nx;
if(!x) fa[nx] = 1;
else {
int y = ch[x][c];
if(l[y] == l[x] + 1) fa[nx] = y;
else {
int ny = ++ cnt; l[ny] = l[x] + 1;
memcpy(ch[ny] , ch[y] , sizeof(ch[y]));
fa[ny] = fa[y]; fa[y] = fa[nx] = ny;
for(; x && ch[x][c] == y ; x = fa[x]) ch[x][c] = ny;
}
}
}
void insert(char * s) {
int n = strlen(s);
for(int i = 0 ; i < n ; ++ i) ins(s[i] - '0');
ins(10);
}
void work() {
for(int i = 1 ; i <= cnt ; ++ i) ++ bac[l[i]];
for(int i = 1 ; i <= 2000000 ; ++ i ) bac[i] += bac[i - 1];
for(int i = 1 ; i <= cnt ; ++ i) T[bac[l[i]] --] = i;
d[1] = 1;
int ans = 0;
for(int i = 1 ; i <= cnt ; ++ i) {
int x = T[i];
ans = (ans + sum[x]) % mod;
for(int j = 0 ; j < 10 ; ++ j) {
if(ch[x][j]) {
d[ch[x][j]] = (d[ch[x][j]] + d[x]) % mod;
sum[ch[x][j]] = (sum[ch[x][j]] + (1ll * sum[x] * 10 % mod + 1ll * d[x] * j % mod) % mod) % mod;
}
}
}
printf("%d\n" , ans);
}
}sam;
char s[N];
int main() {
int t = read();
while(t --) {
scanf("%s" , s);
sam.insert(s);
}
sam.work();
return 0;
}