Hdu 3962 Microgene (AC自动机+矩阵)

题目大意:

构造一个串使得有两个以及两个以上的目标串。长度为L的所有串中有多少个这样的串。


思路分析:

用所有的数量减去只有1个和没有目标串的数量就是答案了。

如果数据很小,可以用dp解。dp[i][j][k] 表示长度为i,走到自动机的j,有k个目标串的数量。

转移便是。

if(j->next[d] ->isword) dp[i+1][j->next][1] += dp[i][j][0].

else dp[i+1][j->next][0]+=dp[i][j][0],dp[i+1][j->next][1] += dp[i][j][1]...

现在长度达到百万。

所以用矩阵优化。

设自动机的节点数量为idx,那么就开一个(2*idx,2*idx)的矩阵。

如果i<idx j<idx 表示 开始在i的时候没有目标串,走到j也没有。

如果i<idx j>idx 表示 开始在i的时候没有目标串,走到j有了一个。

后面同理。。。

那么构造这个矩阵便是按照上面的dp方程类似构造。

if(j->next[d]->isword)matrix [i][j->next->index+idx]++; 开始的时候没有,走过来加一个

else matrix [i][j]++,matrix [i+idx][j+idx] 开始的时候没有,走到j也没有  和  开始的时候有一个,走到j还是一个。

矩阵的构造是难= =


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define N 75
using namespace std;

const int mod = 10007;
const char tab = 'a';
const int max_next = 4;
int rev[256];
struct trie
{
    struct trie *fail;
    struct trie *next[max_next];
    int isword;
    int index;
};
struct AC
{
    trie *que[100005],*root,ac[100005];
    int head,tail;
    int idx;
    trie *New()
    {
        trie *temp=&ac[idx];
        for(int i=0;i<max_next;i++)temp->next[i]=NULL;
        temp->fail=NULL;
        temp->isword=0;
        temp->index=idx++;
        return temp;
    }
    void init()
    {
        idx=0;
        root=New();
    }
    void Insert(trie *root,char *word,int len){
        trie *t=root;
        for(int i=0;i<len;i++){
            if(t->next[rev[word[i]]]==NULL)
                t->next[rev[word[i]]]=New();
            t=t->next[rev[word[i]]];
        }
        t->isword++;
    }
    void acbuild(trie *root){
        int head=0,tail=0;
        que[tail++]=root;
        root->fail=NULL;
        while(head<tail){
            trie *temp=que[head++],*p;
            for(int i=0;i<max_next;i++){
                 if(temp->next[i]){
                    if(temp==root)temp->next[i]->fail=root;
                    else {
                        p=temp->fail;
                        while(p!=NULL){
                            if(p->next[i]){
                                temp->next[i]->fail=p->next[i];
                                break;
                            }
                            p=p->fail;
                        }
                        if(p==NULL)temp->next[i]->fail=root;
                    }
                    if(temp->next[i]->fail->isword)temp->next[i]->isword++;
                    que[tail++]=temp->next[i];
                 }
                 else if(temp==root)temp->next[i]=root;
                 else temp->next[i]=temp->fail->next[i];
            }
        }
    }
    void tra()
    {
        for(int i=0;i<idx;i++)
        {
            if(ac[i].fail!=NULL)printf("fail = %d ",ac[i].fail->index);
            for(int k=0;k<max_next;k++)
                printf("%d ",ac[i].next[k]->index);
            puts("");
        }
    }
}sa;

struct matrix
{
    int r,c;
    int data[N][N];
    matrix(){}
    matrix(int _r,int _c):r(_r),c(_c){memset(data,0,sizeof data);}
    friend matrix operator * (const matrix A,const matrix B)
    {
        matrix res;
        res.r=A.r;res.c=B.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)
        {
            for(int j=0;j<B.c;j++)
            {
                for(int k=0;k<A.c;k++)
                {
                    if(A.data[i][k] && B.data[k][j]){
                        res.data[i][j]+=A.data[i][k]*B.data[k][j];
                        res.data[i][j]%=mod;
                    }
                }
            }
        }
        return res;
    }
    friend matrix operator + (const matrix A,const matrix B)
    {
        matrix res;
        res.r=A.r;res.c=A.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)
        {
            for(int j=0;j<A.c;j++)
            {
                res.data[i][j]=A.data[i][j]+B.data[i][j];
                res.data[i][j]%=mod;
            }
        }
        return res;
    }
    friend matrix operator - (const matrix A,const matrix B)
    {
        matrix res;
        res.r=A.r;res.c=A.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)
        {
            for(int j=0;j<A.c;j++)
            {
                res.data[i][j]=A.data[i][j]-B.data[i][j];
                res.data[i][j]=(res.data[i][j]%mod+mod)%mod;
            }
        }
        return res;
    }
    friend matrix operator ^ (matrix A,int n)
    {
        matrix res;
        res.r=A.r;res.c=A.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)res.data[i][i]=1;

        while(n)
        {
            if(n&1)res=res*A;
            A=A*A;
            n>>=1;
        }
        return res;
    }
    void print()
    {
        for(int i=0;i<r;i++)
        {
            for(int j=0;j<c;j++)
                printf("%d ",data[i][j]);
            puts("");
        }
    }
}E,zero;

char word[30];

int main()
{
    rev['A']=0;
    rev['C']=1;
    rev['G']=2;
    rev['T']=3;
    int n,L;
    while(scanf("%d%d",&n,&L)!=EOF)
    {
        sa.init();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",word);
            sa.Insert(sa.root,word,strlen(word));
        }
        sa.acbuild(sa.root);
        E=matrix(sa.idx*2,sa.idx*2);

        for(int i=0;i<sa.idx*2;i++)E.data[i][i]=1;

        matrix origin=matrix(sa.idx*2,sa.idx*2);

        for(int i=0;i<sa.idx;i++)
        {
            for(int j=0;j<4;j++)
            {
                int temp=sa.ac[i].next[j]->index;
                if(sa.ac[i].next[j]->isword)
                    origin.data[i][temp+sa.idx]++;
                else
                    origin.data[i][temp]++,origin.data[i+sa.idx][temp+sa.idx]++;
            }
        }
        origin.print();

        origin=origin^L;

        int ans=1;
        int x=4;
        int t=L;
        while(t)
        {
            if(t&1)ans=(ans*x)%mod;
            x=(x*x)%mod;
            t>>=1;
        }
        for(int i=0;i<2*sa.idx;i++)
        {
            ans-=origin.data[0][i];
            ans=(ans+mod)%mod;
        }

        printf("%d\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值