【POJ2778】

题解:

  • 首先建好AC自动机
  • f[i][j] 表示匹配到第 i 个字符,当前在AC自动机的编号为j的节点的方案数
  • f[i][j]=f[i1][k] 当且仅当 j节点,k 节点都可行
  • 显然是一个线性递推,构建一个SIZ*SIZ的矩阵(SIZ为AC自动机节点数),其中a[i][j]=1当且仅当可以由i转移到j,即i和j都可行
  • 考虑如何判断一个节点是否为可行状态
  • 如果一个节点是病毒串的结尾,显然是不行的,打上标记
  • 同样的,如果一个节点的fail有标记,即对应串的一个后缀为病毒串也是不行的,打上标记
  • 至此,问题就解决了
  • 复杂度 O(3logn)
//by sdfzchy
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
const int inf=(1<<30),mod=100000;
int m;
LL n;
inline int in()
{
    char ch=getchar();
    int f=1,tmp=0;
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {tmp=(tmp<<1)+(tmp<<3)+(ch-'0');ch=getchar();}
    return tmp*f;
}

char s[1100];

inline int gi(char ch)
{
    if(ch=='A') return 0;
    if(ch=='C') return 1;
    if(ch=='T') return 2;
    if(ch=='G') return 3;
}
int tot;
struct AC_atm
{
    int ch[4],fail,col;
}t[210];

void ins(char *s)
{
    int len=strlen(s),u=0;
    for(int i=0;i<len;i++)
    {
        int c=gi(s[i]);
        if(!t[u].ch[c]) t[u].ch[c]=++tot;
        u=t[u].ch[c];
    }
    t[u].col=1;
}
queue<int> q;
void gi_fail()
{
    for(int i=0;i<4;i++)
        if(t[0].ch[i])
            q.push(t[0].ch[i]),t[t[0].ch[i]].fail=0;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        t[u].col|=t[t[u].fail].col;
        for(int i=0;i<4;i++)
        {
            int &v=t[u].ch[i];
            if(v) t[v].fail=t[t[u].fail].ch[i],q.push(v);
            else  v=t[t[u].fail].ch[i];
        }
    }
}

LL ans;
struct Matrix
{
    LL a[110][110];
    Matrix operator *(const Matrix &b) const
    {
        Matrix c;
        memset(c.a,0,sizeof(c.a));
        for(int i=0;i<=tot;i++)
            for(int j=0;j<=tot;j++)
                for(int k=0;k<=tot;k++)
                    c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
        return c;
    }
}f;

Matrix ksm(Matrix a,LL b)
{
    Matrix ret=a; b--;
    while(b)
    {
        if(b&1) ret=ret*a;
        a=a*a;
        b>>=1;
    }
    return ret;
}

int main()
{
    m=in(); scanf("%lld",&n);
    for(int i=1;i<=m;i++) scanf("%s",s),ins(s);
    gi_fail();
    memset(f.a,0,sizeof(f.a));
    for(int i=0;i<=tot;i++) if(!t[i].col)
        for(int j=0;j<4;j++) if(!t[t[i].ch[j]].col)
            f.a[i][t[i].ch[j]]++;
    Matrix Ans=ksm(f,n);
    for(int i=0;i<=tot;i++) ans=(ans+Ans.a[0][i])%mod;
    printf("%lld\n",ans); 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值