题意:给定 (n) 个字符串 (S_{1cdots n}) ,求所有不同的子串的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0)。答案有可能很大,我们需要对 (10^9 + 7) 取模。
(SAM)
把所有串用间隔字符拼起来,建出 (SAM) ,然后在上面 bfs ,求出每个点的答案。注意我们要减去不合法的转移,即跨越间隔字符的转移。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
register char s; while(!isdigit(s=getchar())) f=s=='-'?-1:f;
do x=x*10+(s^48); while(isdigit(s=getchar())); return x*f;
} const int N=2000010,M=1000000007;
int n,lst=1,tot=1,anss;
int fa[N],c[N][11],len[N],sz[N],sum[N],in[N],ans[N];
char s[N];
inline void add(int ch) {
R p=lst,np=lst=++tot;
len[np]=len[p]+1;
for(;p&&!c[p][ch];p=fa[p]) c[p][ch]=np;
if(!p) fa[np]=1;
else {
R q=c[p][ch];
if(len[q]==len[p]+1) fa[np]=q;
else {
R nq=++tot;
memcpy(c[nq],c[q],11<<2);
fa[nq]=fa[q],len[nq]=len[p]+1;
fa[np]=fa[q]=nq;
for(;p&&c[p][ch]==q;p=fa[p]) c[p][ch]=nq;
}
}
}
queue<int> q;
inline void main() {
n=g();
for(R i=1,len;i<=n;++i) {
scanf("%s",s),len=strlen(s);
for(R i=0;i<len;++i) add(s[i]-48);
add(10);
}
for(R i=1;i<=tot;++i)
for(R ch=0;ch<=10;++ch)
++in[c[i][ch]];
q.push(1);
while(q.size()) {
R u=q.front(); q.pop();
R l=1;
if(u!=1) l=len[u]-len[fa[u]];
for(R i=0;i<=10;++i) if(c[u][i]) {
R v=c[u][i];
if(v) {
if(i!=10)
ans[v]=(ans[v]+ans[u]*10ll+1ll*(l-sum[u]+M)*i)%M,
sum[v]+=sum[u];
else sum[v]+=l;
} if(--in[v]==0) q.push(v);
}
}
for(R i=1;i<=tot;++i) anss=(anss+ans[i])%M;
printf("%d
",anss);
}
} signed main() {Luitaryi::main(); return 0;}
2020.01.10