字符串 后缀自动机+动态规划

来源:互联网 发布:数据库运维工作内容 编辑:程序博客网 时间:2024/06/06 07:00

题意

这里写图片描述
n<=106,|si|<=106

分析

考虑如果现在有一个串T,我们要如何去判断T是否能被构造。贪心地想,肯定是先在第一个串里面找到最长的一个子串,满足该子串是T的前缀,然后再到第二个串找,如此类推。
如果只有一个串的话,找本质不同的子串的其中一种方法就是先建出sam,然后在sam的DAG上dp一下求出从原点到每个点有多少种不同的路径。
拓展到多个串,我们可以先对每个串建出后缀自动机,然后从最后一个串往前dp,每一个串的sam都按照逆拓扑序来dp。设f[u]表示从u这个节点往后走有多少种不同的方案。若u有连出去字符x的边,设连到v,则f[u]+=f[v]。否则,找到后面第一个存在字符x的串,设这个串的原点连出去字符x的边会走到w,则f[u]+=f[w]。
最后第一个串的原点,设为s,f[s]则是答案。

代码

#include<iostream> #include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=3000005;const int MOD=1000000007;int n,cnt,last,ch[N][26],f[N],mx[N],fa[N],numl[N],numr[N],nx[26],a1,a[N],d[N];char s[N];queue<int> que;void ins(int x,int y){    int p,q,np,nq;    p=last;last=np=++cnt;mx[np]=mx[p]+1;    for (;!ch[p][x]&&p;p=fa[p]) ch[p][x]=np;    if (!p) fa[np]=y;    else    {        q=ch[p][x];        if (mx[q]==mx[p]+1) fa[np]=q;        else        {            nq=++cnt;mx[nq]=mx[p]+1;            memcpy(ch[nq],ch[q],sizeof(ch[q]));            fa[nq]=fa[q];            fa[q]=fa[np]=nq;            for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;        }    }}void topsort(int l,int r){    for (int i=l;i<=r;i++)        for (int j=0;j<26;j++)            if (ch[i][j]) d[ch[i][j]]++;    for (int i=l;i<=r;i++) if (!d[i]) que.push(i);    a1=0;    while (!que.empty())    {        int u=que.front();que.pop();a[++a1]=u;        for (int i=0;i<26;i++)            if (ch[u][i])            {                d[ch[u][i]]--;                if (!d[ch[u][i]]) que.push(ch[u][i]);            }    }}int main(){    freopen("str.in","r",stdin);freopen("str.out","w",stdout);    scanf("%d",&n);    for (int i=1;i<=n;i++)    {        scanf("%s",s);        last=++cnt;numl[i]=last;        int len=strlen(s);        for (int j=0;j<len;j++) ins(s[j]-'a',numl[i]);        numr[i]=cnt;    }    for (int i=n;i>=1;i--)    {        topsort(numl[i],numr[i]);        for (int j=a1;j>=1;j--)        {            int x=a[j];            f[x]=1;            for (int k=0;k<26;k++)                if (ch[x][k]) (f[x]+=f[ch[x][k]])%=MOD;                else if (nx[k]) (f[x]+=f[ch[numl[nx[k]]][k]])%=MOD;        }        for (int k=0;k<26;k++)            if (ch[numl[i]][k]) nx[k]=i;    }    printf("%d",f[1]);    return 0;}