【BZOJ1030】【Tyvj1806】文本生成器,AC自动机+DP

传送门1
传送门2
写在前面:明天开始种树吗?
思路:感觉和Wireless Password有点像?f[i][j][k]表示到长度为i的字符串匹配到了自动机的节点j,k=1说明已经有可认识的单词,k=0表示没有。状态方程应该比较好想了吧,如果仍然不懂可以戳一下链接,这个题还不是状压呢。
(快速打完后样例过,但数组开小了导致RE,开大后交上去WA一半,发现建fail指针时对节点i没有处理num[i]和num[fail[i]]的关系,如果num[fail[i]]是1的话那么num[i]也应该是1,加了一步就A了……)
(Po姐的题解说可以用减法原理= =)
注意:好像要注意的已经在思路里说过了……
代码:

#include<bits/stdc++.h>
#define mod 10007
using namespace std;
int n,m,root=1,tot=1;
int trie[11000][26],fail[11000],f[103][10003][2];
char s[110];
bool num[11000];
queue<int>q;
void insert(char s[])
{
    int len=strlen(s),now=root;
    for (int i=0;i<len;i++)
    {
        if (!trie[now][s[i]-'A']) trie[now][s[i]-'A']=++tot;
        now=trie[now][s[i]-'A'];
    }
    num[now]=1;
}
void build()
{
    int now,tmp;
    q.push(root);
    while (!q.empty())
    {
        now=q.front();
        q.pop();
        num[now]|=num[fail[now]];
        for (int i=0;i<26;i++)
        if (trie[now][i])
        {
            tmp=fail[now];
            while (tmp&&!trie[tmp][i]) tmp=fail[tmp];
            if (tmp&&now!=root) fail[trie[now][i]]=trie[tmp][i];
            else fail[trie[now][i]]=root;
            q.push(trie[now][i]);
        }
        else
        {
            if (now==root) trie[now][i]=root;
            else trie[now][i]=trie[fail[now]][i];
        }
    }
}
main()
{
    scanf("%d%d",&n,&m);
    int x,y,z,ans=0;
    for (int i=1;i<=n;i++)
        scanf("%s",s),
        insert(s);
     build();
     f[0][1][0]=1;
     for (int i=0;i<m;i++)
        for (int j=1;j<=tot;j++)
            for (int k=0;k<2;k++)
                for (int l=0;l<26;l++)
                    x=i+1,y=trie[j][l],z=k|num[y],
                    f[x][y][z]=(f[x][y][z]+f[i][j][k])%mod;
    for (int i=1;i<=tot;i++)
    ans=(ans+f[m][i][1])%mod;
    printf("%d",ans);
}

你可能感兴趣的:(【BZOJ1030】【Tyvj1806】文本生成器,AC自动机+DP)