poj2778(AC自动机+矩阵快速幂)

题意:给你n个字符串,问你长度为m的字符串且字符串中不含有那n个子串的字符串的数量

解题思路:这道题一开始就不太懂,还以为是组合数学的题目,后面看了别人的博客,才知道这是属于AC自动机的另一种用法,是关于fail数组的运用,因为题目问的是不允许包含那n个字符串,所以我们可以这么想,假设一个trie树每个结点都有A,T,C,G这四个儿子结点,然后我们把这n个字符串存进trie树里面,字符串的结尾标记一下,然后根据fail数组的构造,如果某个结点fail指向的结点被标记了,那么这个结点也是不允许走的,这样,一个符合条件的trie树就建立出来了,剩下的就是矩阵部分。把题目简化成是从结点0出发到其他结点走n步的的所有允许情况;

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=110;
struct matrix
{
    ll mat[N][N];
    matrix()
    {
        memset(mat,0,sizeof(mat));
    }
}ans,fna;
int trie[N][4];
int fail[N],tot;
bool flag[N];
char s[15];
char c['Z'+1];
int n,m;
void build_trie(char *str)//构建trie树
{
    int root=0;
    int len=strlen(str);
    for(int i=0;i<len;i++)
    {
        int id=c[str[i]];
        if(trie[root][id]==0)
        {
            trie[root][id]=++tot;
            //cout<<tot<<endl;
        }
        root=trie[root][id];
    }
    flag[root]=1;
}
void build_fail()
{
    queue<int>q;
    for(int i=0;i<4;i++)
    {
        if(trie[0][i]!=0)
            q.push(trie[0][i]);
    }
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        if(flag[fail[now]])//如果当前结点的指向是不允许的,那么这个点也是不允许的
            flag[now]=true;
        for(int i=0;i<4;i++)
        {
            if(!trie[now][i])
            {
                trie[now][i]=trie[fail[now]][i];
                continue;
            }
            fail[trie[now][i]]=trie[fail[now]][i];
            q.push(trie[now][i]);
        }
    }
}
matrix mul(matrix x,matrix y)
{
    matrix tmp;
    for(int i=0;i<=tot;i++)
        for(int j=0;j<=tot;j++)
            for(int k=0;k<=tot;k++)
    {
        tmp.mat[i][j]+=x.mat[i][k]*y.mat[k][j];
        tmp.mat[i][j]%=100000;
    }
    return tmp;
}
matrix matrixpow(matrix x,ll k)
{
    matrix ret;
    for(int i=0;i<=tot;i++)
        ret.mat[i][i]=1;
    while(k)
    {
        if(k&1)
            ret=mul(ret,ans);
        ans=mul(ans,ans);
        k>>=1;
    }
    return ret;
}
matrix build_mat()//构建矩阵
{
    matrix temp;
    for(int i=0;i<=tot;i++)
    {
        if(flag[i])
            continue;
        for(int j=0;j<4;j++)
        {
            if(flag[trie[i][j]])continue;
            ++temp.mat[i][trie[i][j]];
        }
    }
    return temp;
}
void init()
{
    memset(fail,0,sizeof(fail));
    memset(trie,0,sizeof(trie));
    tot=0;
    memset(flag,0,sizeof(flag));
    c['A']=0;
    c['T']=1;
    c['C']=2;
    c['G']=3;
}
int main()
{
    init();
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        build_trie(s);
    }
    build_fail();
    ans=build_mat();
    fna=matrixpow(ans,n);
    ll xx=0;
    for(int i=0;i<=tot;i++)
    {
        xx+=fna.mat[0][i];xx%=100000;
    }
    printf("%lld\n",xx);
}

  

代码:

posted @ 2018-11-08 19:16  荒岛的龟  阅读(879)  评论(0编辑  收藏  举报