A自动机C+dp

AC+dp 
bzoj1030 [JSOI2007]文本生成器
题意:给出N个模板串,问生成长为M的不含任意模板串的串的方案数(只有大写字母)
#include <bits/stdc++.h>
#define MOD 10007
using namespace std;
const int mod=10007;
int n,m,tot=0;
int fail[6010],ch[6010][30],f[200][6010];
char w[105];//每次输入的串
bool ed[6010];
int KSM(int a,int p){//快速幂求A的P次方
    int t=1;
    while (p){
        if (p&1)t=(t*a)%mod;
        a=(a*a)%mod;
        p>>=1;
    }
    return t%mod;
}
void build(){//先维护ch[][]即字典树
    int now=0;//0就是ROOT
    int len=strlen(w);//得出当前串长
    for (int i=0;i<len;i++){//逐个字符打进去
        int x=w[i]-'A';//第X条边
        if (!ch[now][x]) ch[now][x]=++tot;//这个儿子是空,记录这个是第TOT个结点
        now=ch[now][x];//然后把那个结点编号记为NOW,则打入下一字符就是从当前NOW进行了
    }
    ed[now]=1;//串输入完后就打上串末标记
}
void make(){//本函数功能是要利用FAIL指针把一些节点编号也打上文末标记,求FAIL指针需要广搜
    queue<int> q;           //开出Q队列
    for (int i=0;i<26;i++)  //扫一次根结点的每个儿子
        if (ch[0][i])       //这个儿了存在
            q.push(ch[0][i]);//就把这个儿子打入队列
    while (!q.empty()){     //Q队列非空
        int r=q.front();    //读出队首
        q.pop();            //然后删去
        for (int i=0;i<26;i++){ //扫一次当前结点r的儿子
            if (!ch[r][i]){     //这个儿子不存在
                ch[r][i]=ch[fail[r]][i];//就把这个儿子更新为其失配指针的该边儿子(那个儿子有两个老豆了)
                continue;   //fail[r]如果未更新,那就是0,指向根即可
            }               //如果这个儿子存在了
            fail[ch[r][i]]=ch[fail[r]][i];//当前r的i边儿子的失配针 指向 当前r的失配针指向的结点的i边儿子
            ed[ch[r][i]]|=ed[fail[ch[r][i]]];//r的i儿失配指向的结点的文末标记 赋给 r的i儿文末标记
            q.push(ch[r][i]);//打入r的i儿
        }
    }
}
void doit(){//DP得出结果,需要先维护ch[][]即字典树及DP时按结点的广搜顺序
    int ans,i,j,k;
    ans=KSM(26,m);//总数,下面再减去不含模板串的串
    f[0][0]=1;
    for (i=0;i<m;i++)         //构造m长度的字符串
        for (j=0;j<=tot;j++)  //AC自动机上的点
            if (!ed[j])       //不是结束字符
                for (k=0;k<26;k++){  //枚举下一个点
                    int x=ch[j][k];  //节点编号
                    if (ed[x]) continue;//如果该结点是串末不用往后跑,因为现在求不包括模板串的方案数
                    f[i+1][x]+=f[i][j];//f[i][j]表示长度为i的字符串与AC自动机上的第j个点匹配的方案数
                    f[i+1][x]%=mod;//f[i+1][x]表示长度为i+1的字符串与AC自动机上的第x个点匹配的方案数,X是J的某儿子
                }
    for (i=0;i<=tot;i++)    //AC自动机上的点
        if (!ed[i])         //非文末结点
           ans+=mod-f[m][i],ans%=mod;//方案数就累加,防止负数每次先加MOD再模上MOD
    printf("%d",ans);
    return;
}
int main(){
    memset(ed,0,sizeof(ed));
    scanf("%d%d",&n,&m);    //N个模板串,生成串长是M
    for (int i=1;i<=n;i++){ //逐个串
        scanf("%s",&w);     //输入串
        build();            //建树
    }
    make();
    doit();
    return 0;
}
Sample Input 
2 2 
A 
B
Sample Output 
100

猜你喜欢

转载自blog.csdn.net/cj1064789374/article/details/85381914