hihocoder #1457 : 后缀自动机四·重复旋律7

题意:给定 \(n\) 个字符串 \(S_{1\cdots 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\n",anss);
}
} signed main() {Luitaryi::main(); return 0;}

2020.01.10

posted @ 2020-01-10 17:47  LuitaryiJack  阅读(122)  评论(0编辑  收藏  举报