POJ 2778 DNA Sequence && AC自動機 矩陣 矩陣加速

暑假剛學完AC自動機模板的時候,看見這道題就感覺這題好難,經過幾次複習與學習,突然發現這題不是那麼的難。

題意:給你10個只由‘A’、‘T’、‘C’、‘G’這四個字母組成的最大長度爲10的字符串。問有多少個不同的長度爲n (1 <= n <=2000000000).的字符串。結果對100000取餘。

解法:之前做的都是狀壓DP,見到這題也想這麼做,突然發現長度略大,DP一定TLE。由於數據量是10^9,本能反應就是log(n)算法,看那衆多大神的題解,學習到,原來矩陣也可以這麼用。先是做一個AC自動機,然後做狀態轉移矩陣:枚舉每一個節點,然後讓這個節點添加一個字母(A、T、C、G)判斷是不是單詞節點,如果不是就把矩陣的對應位置++;如果是單詞節點,那麼就尋找適配路徑之前的節點,同上處理。最後就能得到一個“節點數*節點數”大小的矩陣,每個位置代表從某個狀態轉移到另一個狀態的方案數。最後求矩陣的n次方,需要矩陣加速,也就是快速冪的思路。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define pi acos(-1.0)
#define eps 1e-8
using namespace std;
const int maxnode = 110;
const int charsize = 4;
const int mod = 100000;
int ch[maxnode][charsize], f[maxnode], val[maxnode], last[maxnode];
int sz, n, m;

void init() {memset(ch[0],0,sizeof ch[0]);sz=1;}
int idx(char c) {if(c=='A') return 0; if(c=='T') return 1; if(c=='C') return 2; return 3;}
void insert(char *s, int v = 1)
{
    int u = 0, n = strlen(s);
    for(int i = 0 ; i < n ; ++ i)
    {
        int c = idx(s[i]);
        if(!ch[u][c])
        {
            memset(ch[sz],0,sizeof ch[sz]);
            val[sz] = 0;
            ch[u][c] = sz++;
        }
        u = ch[u][c];
    }
    val[u] = v;
}

void getfail()
{
    queue<int> q;
    f[0] = 0;
    for(int c = 0 ; c < charsize ; ++ c)
    {
        int u = ch[0][c];
        if(u) {f[u]=last[u]=0;q.push(u);}
    }
    while(!q.empty())
    {
       int r = q.front(); q.pop();
       for(int c = 0 ; c < charsize ; ++ c)
       {
           int u = ch[r][c];
           if(!u) { ch[r][c] = ch[f[r]][c];continue;}
           q.push(u);
           int v = f[r];
           while(v&&!ch[v][c]) v = f[v];
           f[u] = ch[v][c];
           last[u] = val[f[u]]?f[u]:last[f[u]];
       }
    }
}

struct mat
{
    long long m[maxnode][maxnode];
    mat operator*(const mat& ma) const
    {
        mat c;
        memset(c.m, 0, sizeof c.m);
        for(int i = 0 ; i < sz ; ++ i)
        {
            for(int j = 0 ; j < sz ; ++ j)
            {
                for(int k = 0 ; k < sz ; ++ k)
                {
                    c.m[i][j] += m[i][k]*ma.m[k][j];
                }
                if(c.m[i][j]>=mod) c.m[i][j]%=mod;
            }
        }
        return c;
    }
}g;
mat pow(mat m, int n)
{
    mat tmp;
    for(int i = 0 ; i < sz ; ++ i)
    {
        for(int j = 0 ; j < sz ; ++ j)
        {
            tmp.m[i][j] = (i==j);
        }
    }

    while(n)
    {
        if(n&1) tmp = tmp*m;
        m = m*m;
        n>>=1;
    }
    return tmp;
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        char s[maxnode];
        init();
        for(int i = 0 ; i < n ; ++ i)
        {
            scanf("%s",s);
            insert(s);
        }
        getfail();
        for(int i = 0 ; i < sz ; ++ i)
        {
            if(val[i]||last[i]) continue;
            for(int c = 0 ; c < charsize ; ++ c)
            {
                int u = ch[i][c];
                if(u)
                {
                    if(!val[u]&&!last[u]) g.m[i][u] ++;
                }
                else
                {
                    int t = f[i];
                    while(t)
                    {
                        int u = ch[t][c];
                        if(u&&!val[u]&&!last[u])
                        {
                            g.m[i][u]++;
                            break;
                        }
                        t = f[t];
                    }
                    if(t==0)
                    {
                        int u = ch[t][c];
                        if(!u&&!val[u]&&!last[u]) g.m[i][u]++;
                        else if(!u) g.m[i][0]++;
                    }
                }
            }
        }
        mat tt = pow(g, m);
        long long ans = 0;
        for(int i = 0 ; i < sz ; ++ i)
        {
            ans += tt.m[0][i];
            ans%=mod;
        }
        printf("%I64d\n",ans);
    }

    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章