1. 程式人生 > >BZOJ 1030 [JSOI2007]文本生成器(AC自動機)

BZOJ 1030 [JSOI2007]文本生成器(AC自動機)

bzoj bsp using www eof 鏈接 strlen h+ cnblogs

【題目鏈接】 http://www.lydsy.com/JudgeOnline/problem.php?id=1030

【題目大意】

  求出包含任意一個給定串的串數量

【題解】

  我們求出不包含任意一個給定串的數量,用全集去減即可,
  對於給定串建立AC自動機,用1節點作為根,0節點向1連全字符集轉移作為超級源,
  那麽0->match能匹配所有不包含給定串的串,
  記dp[i][j]表示匹配了i長度,匹配到AC自動機j節點的串數量,
  統計之後取補集即可。

【代碼】

#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
const int N=30010;
namespace AC_DFA{
    const int Csize=26; 
    int tot,son[N][Csize],sum[N],fail[N],q[N],match[N];
    void Initialize(){
        memset(match,0,sizeof(int)*(tot+1)); 
        memset(fail,0,sizeof(int)*(tot+1));
        memset(sum,0,sizeof(int)*(tot+1));
        for(int i=0;i<=tot;i++)for(int j=0;j<Csize;j++)son[i][j]=0;
        tot=1; 
    }
    inline int Tr(char ch){return ch-‘A‘;}
    int Insert(char *s){
        int x=1;
        for(int l=strlen(s),i=0,w;i<l;i++){
            if(!son[x][w=Tr(s[i])]){
                son[x][w]=++tot;
            }x=son[x][w]; 
        }sum[x]++;
        return x;
    }
    void MakeFail(){
        int h=1,t=0,i,j,x=1; q[++t]=1;
        while(h<=t)for(x=q[h++],i=0;i<Csize;i++)
        if(son[x][i]){
            fail[son[x][i]]=son[fail[x]][i],q[++t]=son[x][i];
            match[son[x][i]]=sum[son[x][i]]?son[x][i]:match[fail[son[x][i]]];
        }else son[x][i]=son[fail[x]][i];
    }
}
using namespace AC_DFA;
const int P=10007;
int dp[110][N];
char s[N];
void Dp(int x){
    for(int i=1;i<=tot;i++){
        if(match[i]||!dp[x-1][i])continue;
        for(int j=0;j<Csize;j++){
            int k=i;
            while(!son[k][j])k=fail[k];
            dp[x][son[k][j]]=(dp[x][son[k][j]]+dp[x-1][i])%P;
        }
    }
}
int n,m; 
int main(){
    scanf("%d%d",&n,&m);
    Initialize();
    for(int i=0;i<Csize;i++)son[0][i]=1;
    for(int i=1;i<=n;i++){scanf("%s",s);Insert(s);}
    MakeFail();
    int ans1=0,ans2=1; 
    dp[0][1]=1;
    for(int i=1;i<=m;i++)Dp(i);
    for(int i=1;i<=m;i++)ans2=(ans2*26)%P;
    for(int i=1;i<=tot;i++)if(!match[i])ans1=(ans1+dp[m][i])%P;
    printf("%d\n",(ans2-ans1+P)%P);
    return 0;
}

BZOJ 1030 [JSOI2007]文本生成器(AC自動機)