poj2778(ac自動機+矩陣快速冪)

POJ2778

題意

求長度爲m且不包含n個子串的種類數.

思路

參考自這個博客.

ac自動機+矩陣快速冪.

這兒有個結論.

給定一個有向圖,問從A點恰好走k步(允許重複經過邊)到達B點的方案數mod p的值
把給定的圖轉爲鄰接矩陣,即A(i,j)=1當且僅當存在一條邊i->j。令C=A*A,那麼C(i,j)=ΣA(i,k)*A(k,j),

實際上就等於從點i到點j恰好經過2條邊的路徑數(枚舉k爲中轉點)。類似地,CA(這裏的C已經是AA)的第i行第j列就表示從i到j經過3條邊的路徑數。

代碼

#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
typedef long long ll;
int tot;
const int mod=100000;
int _size;

struct Matix{
    ll m[110][110];
    Matix(){
        memset(m,0,sizeof(m));
    }
    Matix operator*(const Matix &m2){
        Matix t;
        for(int i=0;i<_size;++i) 
            for(int j=0;j<_size;++j)
                for(int k=0;k<_size;++k)
                    t.m[i][j]=(t.m[i][j]+m[i][k]*m2.m[k][j])%mod;
        return t;
    };
    Matix operator^(ll k) {
        Matix x=*this,ans;
        ans.m[0][0]=ans.m[1][1]=1;
        while(k) {
            if(k&1)
                ans=ans*x;
            x=x*x;
            k>>=1;
        }
        return ans;
    }
}g,res;
struct Trie{
    int next[105010][130],fail[105010];
    bool end[105010];
    int root,L;
    int idx(char s){
        if(s=='A') return 0;
        if(s=='T') return 1;
        if(s=='C') return 2;
        if(s=='G') return 3;
    }
    int newnode(){
        for(int i=0;i<4;i++) next[L][i]=-1;
        end[L++]=0;
        return L-1;
    }
    void init(){
        L=0;
        root=newnode();
    }
    void insert(char buf[]){
        int len=strlen(buf);
        int now=root;
        for(int i=0;i<len;i++){
            int x=idx(buf[i]);
            if(next[now][x]==-1)
                next[now][x]=newnode();
            now=next[now][x];
        }
        end[now]=1;
    }
    void build(){
        queue<int> Q;
        fail[root]=root;
        for(int i=0;i<4;i++){
            if(next[root][i]==-1)
                next[root][i]=root;
            else {
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        }
        while(!Q.empty()){
            int now=Q.front();
            if(end[fail[now]]) end[now]=1;
            Q.pop();
            for(int i=0;i<4;i++){
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
            }
        }
        tot=L;
        for(int i=0;i<L;i++){
            if(end[i]) continue;
            for(int j=0;j<4;j++){
                if(end[next[i][j]]) continue;
                else g.m[i][next[i][j]]++;
            }
        }
        
    }
    
} ac;

char buf[1000010];
int main(){
    int n,m;scanf("%d%d",&n,&m);
    ac.init();
    for(int i=1;i<=n;i++){
        scanf("%s",buf);
        ac.insert(buf);
    }
    ac.build();
    _size=tot;
    for(int i=0;i<tot;i++)
        res.m[i][i]=1;
    while(m){
        if(m&1) res=res*g;
        g=g*g;
        m>>=1;
    }
    long long ans=0;
    for(int i=0;i<tot;i++){
        ans=(ans+res.m[0][i])%mod;
    }
    printf("%lld\n",ans);
    //system("pause");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章