HDU-4436 str2int 后缀自动机
HDU-4436 str2int
题意
给\(n\)个数字串\(s_1,s_2,\dots,s_n\),问有多少个不同的数字(没有前导零)是这\(n\)个数字串中任意一个的子串,求出这些数字的和对2012取模。
\(n\le 10^4,\sum_{i=1}^{n} |s_i|\le 10^5\)
分析
将\(n\)个串用一个没出现过的字符连接起来建后缀自动机,后缀自动机上不同路径数即为不同的子串个数,拓扑排序维护答案即可。
Code
#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=2e5+10;
const int inf=1e9;
int T,n,m;
char s[N];
struct SAM{
int last,cnt;int ch[N][11],fa[N],len[N],sum[N],id[N];
ll g[N],s[N];
int newnode(){
++cnt;
for(int i=0;i<11;i++) ch[cnt][i]=0;
return cnt;
}
void insert(int c){
int p=last,np=newnode();last=np;len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else {
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else{
int nq=newnode();len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void init(){
last=cnt=1;
for(int i=0;i<11;i++) ch[cnt][i]=0;
}
ll solve(){
ll ans=0;
rep(i,1,cnt) g[i]=s[i]=sum[i]=0;
rep(i,1,cnt) sum[len[i]]++;
rep(i,1,cnt) sum[i]+=sum[i-1];
rep(i,1,cnt) id[sum[len[i]]--]=i;
s[1]=1;
rep(i,1,cnt){
int u=id[i];
ans=(ans+g[u])%2012;
for(int j=(u==1);j<10;j++) if(ch[u][j]){
int x=ch[u][j];
g[x]=(g[x]+g[u]*10+s[u]*j)%2012;
s[x]=(s[x]+s[u])%2012;
}
}
return ans;
}
}sam;
int main(){
while(~scanf("%d",&m)){
sam.init();
for(int i=1;i<=m;i++){
scanf("%s",s+1);
int len=strlen(s+1);
rep(j,1,len) sam.insert(s[j]-'0');
sam.insert(10);
}
printf("%lld\n",sam.solve());
}
return 0;
}