【AC自动机+矩阵快速幂】POJ - 2778 - DNA Sequence & HDU - 2243 - 考研路茫茫——单词情结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/82859506

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;
}

猜你喜欢

转载自blog.csdn.net/monochrome00/article/details/82859506