bzoj1030: [JSOI2007]文字生成器(AC自動機+Dp)

Hanks_o發表於2018-02-01

題目傳送門
好題啊。

解法:
直接求很麻煩。
所以轉化為總方案減去不合法的方案。
那麼不合法的方案就相當於在字典樹上面沒有經過結尾節點的路徑條數。
那麼用f[i][j]表示走i步到了第j個節點的方案數。
對應的它下一步走到k。那麼f[i+1][k]+=f[i][j]。
但是會出現一個問題。

就是。
比如說兩個串。
abcd
cdef

那麼當我們匹配了cd的時候其實應該可以繼續匹配e的。
但是假設我們到了d,d是在abcd這條鏈上面的,我們就不能往下了因為下面沒有了(在字典樹上)
所以我們應該d連向e。。這樣走到d時才能繼續匹配e。

然後還有一點要注意的是:
因為我們是在結尾打標記。
然後如果他的fail指標指向的點有標記的話他自己也要打上標記因為你不能走到這裡啊。

程式碼實現:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
struct node{int s,w[30],fail;node(){s=fail=0;memset(w,0,sizeof(w));}}tr[11000];int trlen;
int a[1100000],len;
void bt() {
    int now=0;
    for(int i=1;i<=len;i++) {
        int x=a[i];
        if(tr[now].w[x]==0)tr[now].w[x]=++trlen;
        now=tr[now].w[x];
    }tr[now].s=1;
}
int list[510000],head,tail;
void bfs() {
    head=1,tail=2;list[1]=0;
    while(head!=tail) {
        int x=list[head];
        for(int i=1;i<=26;i++) if(tr[x].w[i]==0)tr[x].w[i]=tr[tr[x].fail].w[i];  //如果我的fail有這個孩子就連邊
            else  {
                if(x!=0)tr[tr[x].w[i]].fail=tr[tr[x].fail].w[i];
                if(tr[tr[tr[x].fail].w[i]].s==1)tr[tr[x].w[i]].s=1;
                list[tail++]=tr[x].w[i];
            }                   
        head++;
    }
}
char s[1100000];
int f[210][11000];
const int mod=10007;
int main() {
    trlen=0;
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%s",s+1);len=strlen(s+1);
        for(int j=1;j<=len;j++)a[j]=s[j]-'A'+1;bt();
    }
    bfs();memset(f,0,sizeof(f));f[0][0]=1;
    for(int i=0;i<m;i++)for(int j=0;j<=trlen;j++) {
        if(tr[j].s==1)continue;
        for(int x=1;x<=26;x++) {
            int k=tr[j].w[x];if(tr[k].s==1)continue;  //不能走到結尾
            f[i+1][k]=(f[i+1][k]+f[i][j])%mod;
        }
    }
    int ans=1;for(int i=1;i<=m;i++)ans*=26,ans%=mod;
    for(int i=0;i<=trlen;i++) if(tr[i].s==0)ans=(ans-f[m][i]+mod)%mod;printf("%d\n",ans);
    return 0;
}

相關文章