1. 程式人生 > >【AC自動機+矩陣快速冪】POJ - 2778 - DNA Sequence & HDU - 2243 - 考研路茫茫——單詞情結

【AC自動機+矩陣快速冪】POJ - 2778 - DNA Sequence & HDU - 2243 - 考研路茫茫——單詞情結

POJ - 2778 - DNA Sequence

題目連結<http://poj.org/problem?id=2778>


題意:

DNA序列只包含ACTG四個字元,已知一些病毒的DNA序列,問你序列長度為n(1 <= n <=2000000000)且不包含病毒的數量。


題解:

對於一張有向圖,恰好走K步(可以走重邊)的方案數就是求鄰接矩陣的k次冪:

  • mat[i][j]表示從i到j走1步的走法。
  • 做一次乘法:mat[i][j]=\sum(mat[i][k]*mat[k][j]),也就是以k為一次中轉,表示從i到j走2步的走法。
  • 那麼mat^{k}就表示從i到j走k步的走法。

AC自動機本身就是一張有向圖,可以作為狀態進行轉移。每個點都有四個方向(加上四個符號),如果沒有沒有包含病毒那麼就是可以走的。建立fail指標的時候可以順便把所有地方的終止符號都打上。


#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const ll N=1e2+7;
const ll mod=100000;
ll tot;
map<char,ll>mp;
struct Mat{
    ll mat[105][105];
}a;
Mat operator *(Mat a,Mat b){
    Mat c;
    memset(c.mat,0,sizeof(c.mat));
    for(ll k=0;k<tot;k++){
        for(ll i=0;i<tot;i++){
            if(a.mat[i][k]==0) continue;
            for(ll j=0;j<tot;j++){
                if(b.mat[k][j]==0) continue;
                c.mat[i][j]=(c.mat[i][j]+(a.mat[i][k]*b.mat[k][j])%mod)%mod;
            }
        }
    }
    return c;
}
Mat operator ^(Mat a,ll b){
    Mat c;
    for(ll i=0;i<tot;i++)
        for(ll j=0;j<tot;j++)
            c.mat[i][j]=(i==j);
    for(ll i=b;i;i>>=1,a=a*a)
        if(i&1) c=c*a;
    return c;
}

struct Trie {
    ll nxt[N][26],fail[N],ed[N];
    ll rt;
    ll newnode(){
        for(ll i=0;i<4;i++)
            nxt[tot][i]=-1;
        ed[tot++]=0;
        return tot-1;
    }
    void init(){
        tot=0;
        rt=newnode();
    }
    void ist(char buf[]){
        ll len=strlen(buf);
        ll now=rt;
        for(ll i=0;i<len;i++){
            ll vi=mp[buf[i]];
            if(nxt[now][vi]==-1)
                nxt[now][vi]=newnode();
            now=nxt[now][vi];
        }
        ed[now]++;
    }
    void build(){
        queue<ll>q;
        fail[rt]=rt;
        for(ll i=0;i<4;i++){
            if(nxt[rt][i]==-1)
                nxt[rt][i]=rt;
            else{
                fail[nxt[rt][i]]=rt;
                q.push(nxt[rt][i]);
            }
        }
        while(!q.empty()){
            ll now=q.front();
            if(ed[fail[now]]) ed[now]=1;
            q.pop();
            for(ll i=0;i<4;i++){
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else{
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    void bmat(){
        memset(a.mat,0,sizeof(a.mat));
        for(ll u=0;u<tot;u++){
            if(ed[u]) continue;
            for(ll i=0;i<4;i++){
                ll v=nxt[u][i];
                if(ed[v]==0)
                    a.mat[u][v]++;
            }
        }
    }
}ac;
char buf[N];

int main() {
    ll n,m;
    ac.init();
    mp['A']=0;mp['C']=1;
    mp['T']=2;mp['G']=3;
    scanf("%lld%lld",&n,&m);
    for(ll i=0;i<n;i++){
        scanf("%s",buf);
        ac.ist(buf);
    }
    ac.build();
    ac.bmat();
    a=a^m;
    ll ans=0;
    for(ll i=0;i<tot;i++)
        ans=(ans+a.mat[0][i])%mod;
    printf("%lld\n",ans);
    return 0;
}

HDU - 2243 - 考研路茫茫——單詞情結

題目連結<http://acm.hdu.edu.cn/showproblem.php?pid=2243>


題意:

與上一題很類似。

給出若干個字串詞根,求出長度不超過L,且至少包括一個詞根的字串個數。


題解:

做法也和上一題基本一樣,這題需要多加一個求字首和的運算,只需要修改一下快速冪就行了。

總數是24^{L},減去求出來的不包含詞根的個數即可。

另外這題要用unsigned long long,並且自帶取模。


#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
#include <map>
using namespace std;
typedef unsigned long long ll;
const ll N=1e2+7;
ll tot;
struct Mat{
    ll mat[105][105];
}a;
Mat operator *(Mat a,Mat b){
    Mat c;
    memset(c.mat,0,sizeof(c.mat));
    for(ll k=0;k<tot;k++){
        for(ll i=0;i<tot;i++){
            if(a.mat[i][k]==0) continue;
            for(ll j=0;j<tot;j++){
                if(b.mat[k][j]==0) continue;
                c.mat[i][j]=(c.mat[i][j]+(a.mat[i][k]*b.mat[k][j]));
            }
        }
    }
    return c;
}
Mat operator +(Mat a,Mat b){
    Mat c;
    for(ll i=0;i<tot;i++)
        for(ll j=0;j<tot;j++)
            c.mat[i][j]=(a.mat[i][j]+b.mat[i][j]);
    return c;
}
Mat operator ^(Mat a,ll b){
    Mat c,t=a;
    memset(c.mat,0,sizeof(c.mat));
    for(ll i=b;i;i>>=1,a=a*a){
        if(i&1) c=t+c*a;
        t=t+t*a;
    }
    return c;
}

struct Trie {
    ll nxt[N][26],fail[N],ed[N];
    ll rt;
    ll newnode(){
        for(ll i=0;i<26;i++)
            nxt[tot][i]=-1;
        ed[tot++]=0;
        return tot-1;
    }
    void init(){
        tot=0;
        rt=newnode();
    }
    void ist(char buf[]){
        ll len=strlen(buf);
        ll now=rt;
        for(ll i=0;i<len;i++){
            ll vi=buf[i]-'a';
            if(nxt[now][vi]==-1)
                nxt[now][vi]=newnode();
            now=nxt[now][vi];
        }
        ed[now]++;
    }
    void build(){
        queue<ll>q;
        fail[rt]=rt;
        for(ll i=0;i<26;i++){
            if(nxt[rt][i]==-1)
                nxt[rt][i]=rt;
            else{
                fail[nxt[rt][i]]=rt;
                q.push(nxt[rt][i]);
            }
        }
        while(!q.empty()){
            ll now=q.front();
            if(ed[fail[now]]) ed[now]=1;
            q.pop();
            for(ll i=0;i<26;i++){
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else{
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    void bmat(){
        memset(a.mat,0,sizeof(a.mat));
        for(ll u=0;u<tot;u++){
            if(ed[u]) continue;
            for(ll i=0;i<26;i++){
                ll v=nxt[u][i];
                if(ed[v]==0)
                    a.mat[u][v]++;
            }
        }
    }
}ac;
char buf[N];
ll qpow(ll a,ll b){
    ll ans=0;
    ll t=a;
    for(ll i=b;i;i>>=1,a=a*a){
        if(i&1) ans=t+ans*a;
        t=t+t*a;
    }
    return ans;
}
int main() {
    ll n,m;
    while(scanf("%llu%llu",&n,&m)!=EOF){
        ac.init();
        for(ll i=0;i<n;i++){
            scanf("%s",buf);
            ac.ist(buf);
        }
        ac.build();
        ac.bmat();
        a=a^m;
        ll ans=qpow(26,m);
        for(ll i=0;i<tot;i++)
            ans=ans-a.mat[0][i];
        printf("%llu\n",ans);
    }
    return 0;
}