bzoj 1030 [JSOI2007]文本生成器 (Trie图+DP)

11 篇文章 0 订阅
1 篇文章 0 订阅

题目大意:给你一堆字符串,一个串不合法的条件是这些字符串中任意一个是这个串的子串,求合法的串的数量

其实这道题比 [HNOI2008]GT考试 那道题好写一些,但道理是一样的

只不过这道题的答案可以转化为 所有可能的字符串(26^m)数量 - 不合法的字符串数量

定义f[i][j]表示匹配到了第i个字符,现在在Trie树上匹配到了第j个节点的方案数

GT考试是跳Next,每次找出 和 插入这个字符后形成的字符串 具有相同最长后缀的位置

那么对于Trie图来说,这不就是fail指针么

Trie树被补全成Trie树后

如果在原来的Trie树中某个节点x,它并没有儿子ch[x][c],那么在补全后,ch[x][c]自动指向的是x的fail指针指向的c儿子,即ch[fail[x]][c]

这不正是我们想要转移的位置么,非常智能

总结:字符串套KMP/AC自动机/Trie图的题,通常都有f[i][j]表示文本串匹配到了第i位,模式串匹配到了第j位这类状态,且有一些题可以用矩阵乘法优化。 

#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long 
#define N 6010
#define M 28
#define mod 10007
#define ui unsigned int
#define idx(x) (x-'A'+1)
#define inf 0x3f3f3f3f
using namespace std;
//re
int n,m;
char str[65][110];
int f[120][N];
int qpow(int x,int y){
    int ans=1;
    while(y){
        if(y&1) ans=(ans*x)%mod;
        x=(x*x)%mod,y>>=1;
    }return ans;
}
struct Trie{
    int ch[N][M],fa[N],fail[N],ed[N],tot;
    void Build()
    {
        for(int i=1;i<=n;i++)
        {
            int len=strlen(str[i]+1),x=0;
            for(int j=1;j<=len;j++)
            {
                int c=idx(str[i][j]);
                if(!ch[x][c])
                    tot++,ch[x][c]=tot,fa[tot]=x;
                x=ch[x][c];
                if(j==len) ed[x]=1;
            }
        }
    }
    void Fail()
    {
        queue<int>q;
        for(int i=1;i<=26;i++)
            if(ch[0][i]) q.push(ch[0][i]);
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=1;i<=26;i++)
                if(ch[x][i])
                    fail[ch[x][i]]=ch[fail[x]][i],
                    q.push(ch[x][i]);
                else 
                    ch[x][i]=ch[fail[x]][i];
        } 
    }
    int solve()
    {
        f[0][0]=1;
        queue<int>q;
        for(int i=0;i<=m;i++)
            for(int x=0;x<=tot;x++)
                for(int c=1;c<=26;c++)
                {
                    int flag=1;
                    for(int k=ch[x][c];k;k=fail[k])
                        if(ed[k]){flag=0;break;}
                    if(!flag) continue;
                    (f[i+1][ch[x][c]]+=f[i][x])%=mod;
                }
        int ans=0;     
        for(int x=0;x<=tot;x++)
            if(!ed[x]) (ans+=f[m][x])%=mod;
        return ans;
    }
}t;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
    t.Build();
    t.Fail();
    printf("%u\n",(qpow(26,m)-t.solve()+mod)%mod);
    return 0;
}





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值