码迷,mamicode.com
首页 > 其他好文 > 详细

文本生成器

时间:2019-11-04 22:00:27      阅读:155      评论:0      收藏:0      [点我收藏+]

标签:using   必须   class   ref   empty   end   比较   生成   匹配   

https://loj.ac/problem/10063

题目描述

??给出\(N\)个单词和文本长度\(M\),求有多少文本满足其内至少包含一个单词,答案对\(10007\)取余。

思路

??直接求满足的文本比较困难,我们考虑求答案的补集,也就是不包含任何一个单词的文本串的数量。对于这个答案我可以用\(dp\)求解,但考虑对单词的查找我们需要用\(A\)C自动机解,因此题目就比较明显了,\(AC\)自动机上跑\(dp\),我们用\(dp[i][j]\)表示文本长度为\(i\),文本的最后一个字符位于\(Trie\)图上的节点编号为\(j\)时的方案数。所以转移方程就比较简单了,\(dp[i+1][u]+=dp[i][v]\)(\(u\)能从\(v\)转移过来),而这个方程的求法与区间\(dp\)类似,我们第一重枚举长度,因为每一个解必定由更短的长度转移过来。

??不过有点必须阐明,我们虽然在\(dp\)方程中有一维枚举的是\(Trie\)图上的节点编号,实际还是每一个字母,但有一个类似匹配的过程,因此这个\(dp\)方程是正确的,它必定不会包含重复的状态,也就可以求出正确答案。

代码

#include<bits/stdc++.h>
using namespace std;
const int mod=10007;

int ch[6100][26],tot=1;
bool ed[6100];
void insert(char *s)
{
    int u=1,len=strlen(s);
    for(int i=0;i<len;i++)
    {
        int c=s[i]-'A';
        if(!ch[u][c])ch[u][c]=++tot;
        u=ch[u][c];
    }
    ed[u]=1;
}

int qpow(int a,int b)
{
    int res=1;
    for(;b;b>>=1)
    {
        if(b&1)res=res*a%mod;
        a=a*a%mod;
    }
    return res;
}

int nxt[6100];
void getfail()
{
    for(int i=0;i<26;i++)
        ch[0][i]=1;
    queue<int>q;
    q.push(1);nxt[1]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<26;i++)
        {
            if(!ch[u][i])ch[u][i]=ch[nxt[u]][i];
            else
            {
                int v=nxt[u];
                q.push(ch[u][i]);
                while(v&&!ch[v][i])v=nxt[v];
                nxt[ch[u][i]]=ch[v][i];
            }
        }
    }
    for(int i=1;i<=tot;i++)
    {
        int v=nxt[i];
        while(v)ed[i]|=ed[v],v=nxt[v];        //对所有节点的ed标记进行传递 
    }
}

int dp[110][6010];
char s[110];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf(" %s",s);
        insert(s);
    }
    getfail();
    dp[0][1]=1;
    for(int i=0;i<m;i++)
        for(int j=1;j<=tot;j++)
            for(int k=0;k<26;k++)
                if(!ed[j]&&!ed[ch[j][k]])dp[i+1][ch[j][k]]=(dp[i+1][ch[j][k]]+dp[i][j])%mod;//枚举的两个点必定不是单词结尾 
    int ans=qpow(26,m);
//    for(int i=1;i<=tot;i++)
//        cout<<dp[m][i]<<endl;
    for(int i=1;i<=tot;i++)
        ans=(ans-dp[m][i]+mod)%mod;
    printf("%d",ans);
}

文本生成器

标签:using   必须   class   ref   empty   end   比较   生成   匹配   

原文地址:https://www.cnblogs.com/fangbozhen/p/11794748.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!