[BZOJ1030]文本生成器 做題筆記

題目來源:http://www.lydsy.com/JudgeOnline/problem.php?id=1030
f[i][j],i表示走到第幾步,j表示走到樹上的哪個結點,j不能是單詞結點或沿fail指針能找到單詞的點。
對每一個f[i-1][j],處理每一個它所能到達的狀態f[i][x],x表示j繼續匹配a-z所能到達的結點。
最後統計f[m][j]之和,用總方案數減去之,即爲答案。
有一個問題,如果文章裏存在模式串裏沒有的字母怎麼辦?

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int mod=10007,N=6100;
int f[101][N];
int A[N][26],sum[N],fail[N],cnt;
bool danger[N];
queue<int> q;
char s[N];
int n,m,ans1,ans2=1;
void ins (char *s) {
    int n=strlen(s),p=1,c;
    for (int i=0;i<n;i++) {
        c=s[i]-'A';
        if (!A[p][c]) A[p][c]=++cnt;
        p=A[p][c];
    }
    danger[p]=1;
}
void getfail () {
    int p; fail[1]=0;
    q.push(1);
    while (!q.empty()) {
        p=q.front(); q.pop();
        for (int i=0;i<26;i++) {
            if (!A[p][i]) continue;
            int k=fail[p];
            while (!A[k][i]) k=fail[k];
            fail[A[p][i]]=A[k][i];
            if (danger[A[k][i]]) danger[A[p][i]]=1;
            q.push(A[p][i]);
        }
    }
}

void dp (int x) {
    for (int i=1;i<=cnt;i++) {
        if (danger[i]||!f[x-1][i]) continue;
        for (int j=0;j<26;j++) {
            int k=i;
            while (!A[k][j]) k=fail[k];
            f[x][A[k][j]]=(f[x][A[k][j]]+f[x-1][i])%mod;
        } 
    }
}

int main () {
    scanf("%d%d",&n,&m);
    cnt=1;
    for (int i=0;i<26;i++) A[0][i]=1;
    for (int i=1;i<=n;i++) {
        scanf("%s",s);
        ins(s);
    }
    getfail();
    f[0][1]=1;
    for (int i=1;i<=m;i++) dp(i);
    for (int i=1;i<=m;i++) 
        ans2=(ans2*26)%mod;
    for (int i=1;i<=cnt;i++) 
        if (!danger[i]) ans1=(ans1+f[m][i])%mod;
    printf("%d",(ans2-ans1+mod)%mod);
}
//實際上如果文章裏有模式串中不存在的字母,那這一部分字母會被統計至編號爲1的結點,最後統計時也會算上
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章