1. 程式人生 > >【學術篇】SPOJ GEN Text Generator AC自動機+矩陣快速冪

【學術篇】SPOJ GEN Text Generator AC自動機+矩陣快速冪

還有5天省選才開始點字串這棵技能樹是不是太晚了點...

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標記即可.

下面就是隆重登場的1A程式碼:
其實我的spoj賬號因為之前測試自動提交指令碼的緣故已經慘不忍睹了..然而一大部分都已經被我disqualify掉了2333~

#include <queue>
#include <cstdio>
#include <cstring>
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);
    }
}