[BJOI2017]魔法咒语

一、题目

点此看题

二、解法

观察数据范围,发现这道题需要对数据分类讨论。

0x01 l<=100

AC \text{AC} 自动机上 d p dp ,设 f [ i ] [ j ] f[i][j] 为长度为 i i ,在自动机上匹配到了点 j j ,暴力转移即可。

0x02 l<=1e8 且 基本词汇长度不超过2

这种数据最大的特点就是 f [ i ] f[i] 只可能从 f [ i 1 ] , f [ i 2 ] f[i-1],f[i-2] 转移过来,并且数据范围提示你要用矩阵乘法。
在这里插入图片描述
构造方法如图所示,左边是转移矩阵,右边是初始矩阵。根据转移方程: f [ i + l e n ] [ t o ] = f [ i ] [ j ] f[i+len][to]=f[i][j] ,我们把转移矩阵的对应位加一(需要对长度讨论,比如长度为 1 1 时, A [ t o ] [ j ] + + A[to][j]++ ),表示 j j 转移到 t o to 。最开始的 f f 特殊算就行了,具体操作可以参考我的代码。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int M = 105;
const int MOD = 1e9+7;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,l,ans,to[M];
char s[M][M],t[M];
void mod(int &x)
{
    x%=MOD;
}
struct Matrix
{
    int n,m,a[2*M][2*M];
    Matrix()
    {
        n=m=0;
        memset(a,0,sizeof a);
    }
    Matrix operator * (const Matrix &B) const
    {
        Matrix r;
        r.n=n;
        r.m=B.m;
        for(int i=0; i<=n; i++)
            for(int j=0; j<=m; j++)
                for(int k=0; k<=B.m; k++)
                    r.a[i][k]=(r.a[i][k]+1ll*a[i][j]*B.a[j][k])%MOD;
        return r;
    }
    Matrix qkpow(int b)
    {
        Matrix res,a=*this;
        res.n=res.m=a.n;
        for(int i=0; i<=a.n; i++) res.a[i][i]=1;
        while(b>0)
        {
            if(b&1) res=res*a;
            a=a*a;
            b>>=1;
        }
        return res;
    }
    void print()
    {
        for(int i=0; i<=n; i++,puts(""))
            for(int j=0; j<=m; j++)
                printf("%d ",a[i][j]);
    }
} F,A;
struct automaton
{
    int cnt,c[M][26],fail[M],val[M],f[M][M];
    void ins(char *s)
    {
        int len=strlen(s),now=0;
        for(int i=0; i<len; i++)
        {
            int v=s[i]-'a';
            if(!c[now][v]) c[now][v]=++cnt;
            now=c[now][v];
        }
        val[now]=1;
    }
    void build()
    {
        queue<int> q;
        for(int i=0; i<26; i++) if(c[0][i]) q.push(c[0][i]);
        while(!q.empty())
        {
            int t=q.front();
            q.pop();
            val[t]|=val[fail[t]];
            for(int i=0; i<26; i++)
                if(c[t][i]) fail[c[t][i]]=c[fail[t]][i],q.push(c[t][i]);
                else c[t][i]=c[fail[t]][i];
        }
    }
    void dp1()
    {
        f[0][0]=1;
        for(int i=0; i<l; i++)
            for(int j=0; j<=cnt; j++)
                for(int k=1; k<=n; k++)
                {
                    int now=j,len=strlen(s[k]);
                    if(i+len>l) continue;
                    for(int o=0; o<len; o++)
                    {
                        now=c[now][s[k][o]-'a'];
                        if(val[now]) goto In;
                    }
                    mod(f[i+len][now]+=f[i][j]);
In:
                    ;
                }
        for(int i=0; i<=cnt; i++)
            mod(ans+=f[l][i]);
        printf("%d\n",ans);
    }
    void dp2()
    {
        A.n=A.m=F.n=2*cnt+1;
        for(int i=0; i<=cnt; i++)
            for(int j=1; j<=n; j++)
            {
                if(val[i]) continue;
                int now=i,len=strlen(s[j]);
                for(int k=0; k<len; k++)
                {
                    now=c[now][s[j][k]-'a'];
                    if(val[now]) goto In;
                }
                if(len==1) A.a[now][i]++;
                else A.a[now][i+cnt+1]++;
In:
                ;
            }
        for(int i=0; i<=cnt; i++)
            A.a[i+cnt+1][i]=1;
        for(int i=1; i<=n; i++)
        {
            int now=0,len=strlen(s[i]);
            if(len>1) continue;
            for(int j=0; j<len; j++)
            {
                now=c[now][s[i][j]-'a'];
                if(val[now]) break;
            }
            if(!val[now]) F.a[now][0]++;
        }
        F.a[cnt+1][0]=1;
        F=A.qkpow(l-1)*F;
        for(int i=0; i<=cnt; i++)
            mod(ans+=F.a[i][0]);
        printf("%d\n",ans);
    }
} AC;
signed main()
{
    n=read();
    m=read();
    l=read();
    for(int i=1; i<=n; i++)
        scanf("%s",s[i]);
    for(int i=1; i<=m; i++)
    {
        scanf("%s",t);
        AC.ins(t);
    }
    AC.build();
    if(l<=100)
        AC.dp1();
    else
        AC.dp2();
}
发布了257 篇原创文章 · 获赞 13 · 访问量 6742

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/104125798
今日推荐