加载中...
题解 SP1676 【GEN - Text Generator】

题目の传送门

前言

AC自动机不想讲了QAQ.其实很久以前是学过然后打过板子的, 但也仅限于打过板子了~

(之前莫名其妙学了一个指针版的但是好像不能用循环遍历fail好像就啥也干不了于是改成了数组…)

其实就是Trie树上挂fail指针… 然后可以完成多串的kmp的样子…

直接看题吧。

题目大意:

求长度为$L$的,包含给定的$n$个短串中的至少一个的字符串的数量。

考虑补集转化, 考虑含这些短串的字符串的数量. 然后用所有长度为$L$的字符串($26^L$)的数量减掉就行了.

然后建立AC自动机, 对于不存在的节点我们就直接让它指向根就可以了.

令$f[i][j]$表示在trie树上从$i$走一步到$j$不经过有isend标记的节点的方案数, 我们遍历AC自动机就可以求出这个矩阵(算是临接矩阵咯~)。

然后根据临接矩阵的特点, 我们求出$f^L[i][j]$就是从$i$开始走$L$步到$j$时合法的方案数。

然后我们最后要求的数量就是$ans=\sum_{i=1}^tf[root][i]$, 其中$t$表示Trie树上的节点数, 最后我们用$26^L - ans$即可.

有一个优化就是如果一个点的fail指针指向了一个有isend标记的节点, 那这个点也是不可达的(由fail指针的定义显然), 我们就也把它贴上isend标记即可。

代码:


#include<bits/stdc++.h>
using namespace std;
const int N=66;
const int p=10007;
char c[10];
int ch[N][26],fail[N],tot;
bool isend[N];
struct mat{int ma[N][N];}a;
inline void init(){
    memset(ch,0,sizeof ch); tot=0;
    memset(fail,0,sizeof fail);
    memset(isend,0,sizeof isend);
    memset(a.ma,0,sizeof a.ma);
}
inline void inser(char* s){
    int len=strlen(s),now=0;
    for(int i=0;i<len;++i){
        int k=s[i]-'A';
        if(!ch[now][k]) ch[now][k]=++tot;
        now=ch[now][k];
    } isend[now]=1;
}
inline void make_fail(){
    queue<int> q;
    for(int i=0;i<26;++i)
        if(ch[0][i]) q.push(ch[0][i]),fail[ch[0][i]]=0;
    while(!q.empty()){
        int now=q.front(); q.pop();
        if(isend[fail[now]]) isend[now]=1;
        for(int i=0;i<26;++i){
            if(ch[now][i]) fail[ch[now][i]]=ch[fail[now]][i],q.push(ch[now][i]);
            else ch[now][i]=ch[fail[now]][i];
        }
    }
}
inline void make_mat(){
    for(int i=0;i<=tot;++i)
        for(int j=0;j<26;++j)
            if(!isend[ch[i][j]])
                ++a.ma[i][ch[i][j]];
}
mat mat_mul(mat a,mat b){ mat c; int ans;
    for(int i=0;i<=tot;++i)
        for(int j=0;j<=tot;++j){
            ans=0;
            for(int k=0;k<=tot;++k){
                ans+=a.ma[i][k]*b.ma[k][j];
                while(ans>=p) ans-=p;
            }
            c.ma[i][j]=ans;
        }
    return c;
}
inline mat mat_pow(mat a,int b){ mat s=a;
    for(--b;b;b>>=1,a=mat_mul(a,a))
        if(b&1) s=mat_mul(s,a);
    return s;
}
inline int qpow(int a,int b,int s=1){
    for(a%=p;b;b>>=1,a=a*a%p)
        if(b&1) s=s*a%p;
    return s;
}
int main(){ int n,l;
    while(scanf("%d%d",&n,&l)!=EOF){
        init();
        for(int i=0;i<n;++i){
            scanf("%s",c); inser(c);
        } make_fail();
        make_mat();
        mat b=mat_pow(a,l);
        int ans=qpow(26,l);
        for(int i=0;i<tot;++i){
            ans-=b.ma[0][i];
            if(ans<0) ans=ans+p;
        }
        printf("%d\n",ans%p);
    }
}

后记:

其实我的spoj账号因为之前测试自动提交脚本的缘故已经惨不忍睹了..然而一大部分都已经被我disqualify掉了2333~
求点赞和评论QAQ


  目录
劳动
快乐