Hdu 2243 考研路茫茫——单词情结 (AC自动机+矩阵)

哎哟喂,中文题。。。不说题意了。

首先做过POJ 2778可以知道AC自动机是可以求出长度为L的串中不含病毒串的数量的。

POJ 2778的大概思路就是先用所有给的病毒串建一个AC自动机,然后将AC自动机上所有非单词节点连一个边。

离散数学中有说道,如果矩阵A 中的 [i][j] 表示 i节点通过一条边可以走到j节点的方法数。

那么A*A这个矩阵的[i][j]就表示 i 节点到j 节点通过两条边可以走到j节点的方法数。

既然知道这个方法,我们就明确要求什么。

ans= 26+26^2+26^3+....+26^L - 长度为1不含病毒串的数量-长度为2不含病毒串的数量-...-长度为L不含病毒串的数量。

解决两个问题

对 2^64取模。知道2^64是 long long 的最大值,那么我们直接开成unsigned long long ...然后放心大胆的运算,溢出便是取模。

我们知道矩阵 matrix ^ L 但是要求出所有的和,就要用矩阵里套矩阵。也就是求矩阵的和。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define N 35
using namespace std;
typedef unsigned long long ll;
const char tab = 'a';
const int max_next = 26;
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[word[i]-tab]==NULL)
                t->next[word[i]-tab]=New();
            t=t->next[word[i]-tab];
        }
        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;
    ll 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[10];
struct supermatrix
{
    matrix ret[2][2];
    friend supermatrix operator * (const supermatrix A,const supermatrix B)
    {
        supermatrix res;
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)res.ret[i][j]=zero;

        for(int i=0;i<2;i++)
        {
            for(int j=0;j<2;j++)
            {
                for(int k=0;k<2;k++)
                {
                    res.ret[i][j]=res.ret[i][j]+A.ret[i][k]*B.ret[k][j];
                }
            }
        }
        return res;
    }
    friend supermatrix operator + (const supermatrix A,const supermatrix B)
    {
        supermatrix res;
        for(int i=0;i<2;i++)for(int j=0;j<2;j++)res.ret[i][j]=zero;
        for(int i=0;i<2;i++)
        {
            for(int j=0;j<2;j++)
            {
                res.ret[i][j]=A.ret[i][j]+B.ret[i][j];
            }
        }
        return res;
    }
    friend supermatrix operator ^ (supermatrix A,ll n)
    {
        supermatrix res;
        for(int i=0;i<2;i++)for(int j=0;j<2;j++)res.ret[i][j]=zero;
        for(int i=0;i<2;i++)res.ret[i][i]=E;
        while(n)
        {
            if(n&1)res=res*A;
            A=A*A;
            n>>=1;
        }
        return res;
    }
};

int main()
{
    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,sa.idx);
        for(int i=0;i<N;i++)E.data[i][i]=1;
        zero=matrix(sa.idx,sa.idx);

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

        for(int i=0;i<sa.idx;i++)
        {
            if(sa.ac[i].isword==0)
            {
                for(int d=0;d<max_next;d++)
                {
                    int temp=sa.ac[i].next[d]->index;
                    if(sa.ac[i].next[d]->isword==0)
                    {
                        origin.data[i][temp]++;
                    }
                }
            }
        }
        supermatrix A;
        A.ret[0][0]=A.ret[0][1]=E;
        A.ret[1][0]=zero;
        A.ret[1][1]=origin;

        A=A^L;
        supermatrix f;
        f.ret[0][0]=f.ret[0][1]=f.ret[1][1]=zero;
        f.ret[1][0]=origin;
        matrix fans=A.ret[0][0]*zero+A.ret[0][1]*origin;
        ll ans=0;
        for(int i=0;i<sa.idx;i++)
        {
            if(sa.ac[i].isword==0)
            {
                ans+=fans.data[0][i];
            }
        }

        matrix I=matrix(2,2);
        I.data[0][0]=I.data[0][1]=1;
        I.data[1][1]=26;
        I.data[1][0]=0;
        I=I^L;

        printf("%I64u\n",I.data[0][1]*26-ans);
    }
    return 0;
}
时间: 2024-08-02 23:02:42

Hdu 2243 考研路茫茫——单词情结 (AC自动机+矩阵)的相关文章

hdu 2243 考研路茫茫——单词情结 ac自动机+矩阵快速幂

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243 题意:给定N(1<= N < 6)个长度不超过5的词根,问长度不超过L(L <231)的单词中至少含有一个词根的单词个数:结果mod 264. 基础:poj 2778DNA 序列求的是给定长度不含模式串的合法串的个数:串长度相当,都到了int上界了: 1.mod 264直接使用unsigned long long自然溢出即可:说的有些含蓄..并且也容易想到是直接使用内置类型,要不然高精度的

hdu 2243 考研路茫茫——单词情结(AC自动+矩阵)

考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4843    Accepted Submission(s): 1527 Problem Description 背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了.一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如

HDU 2243 考研路茫茫――单词情结 (AC自动机 + dp)

HDU 2243 考研路茫茫――单词情结 题意:给定一些词根,如果一个单词包含有词根,则认为是有效的.现在问长度不超过L的单词里面,有多少有效的单词? 思路:这道题和POJ 2778是同样的思路.POJ 2778是要找出长度为L的单词里面有多少无效的单词.那么根据同样的方法构造矩阵,然后所有无效的单词个数为 A + A^2 + ... + A^l 个.而所有单词的个数为26 + 26^2 + - + 26^l 个.两个减一下即为答案. 矩阵连乘求和:I + A^2 + A^3 + ... + A

HDU 2243 考研路茫茫——单词情结(AC自动机+DP+快速幂)

题目链接 错的上头了... 这题是DNA的加强版,26^1 +26^2... - A^1-A^2... 先去学了矩阵的等比数列求和,学的是第二种方法,扩大矩阵的方法.剩下就是各种模板,各种套. #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <algorithm> #include <vector> #include &l

HDU 2243 考研路茫茫――单词情结 (AC自动机 + 矩阵快速幂)

题目链接:考研路茫茫――单词情结 做本题前,个人建议先做一下POJ 2778 http://blog.csdn.net/u013446688/article/details/47378255 POJ2778 是求长度为n,不包含模式串的字符串个数. 而本题是求长度为n,包含模式串的字符串个数.直接用字符串总数减去不包含模式串的字符串个数即为所求. 同样是AC自动机 + 矩阵快速幂.但是还是有所不同的. 因为对2^64取模,所以定义数据类型为unsigned long long就可以了,这样就实现

HDU 2243 考研路茫茫――单词情结 ——(AC自动机+矩阵快速幂)

和前几天做的AC自动机类似. 思路简单但是代码200余行.. 假设solve_sub(i)表示长度为i的不含危险单词的总数. 最终答案为用总数(26^1+26^2+...+26^n)减去(solve_sub(1)+solve(2)+...+solve_sub(n)).前者构造f[i]=f[i-1]*26+26然后矩阵快速幂即可(当然也可以分治的方法).后者即构造出dp矩阵p,然后计算(p^1+p^2+...+p^n),对其分治即可. 代码如下: 1 #include <stdio.h> 2 #

HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)

背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般表示"相反,变坏,离去"等. 于是Lele想,如果背了N个词根,那这些词根到底会不会在单词里出现呢.更确切的描述是:长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义. 比如一共有2个词根 aa 和 ab ,则可能存在104个长度不

HDU 2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

http://acm.hdu.edu.cn/showproblem.php?pid=2243 题意: 给出m个模式串,求长度不超过n的且至少包含一个模式串的字符串个数. 思路: 如果做过poj2778的话,那么这题相对来说就会容易一些. 如果直接去计算的话,情况很复杂,和poj2778一样,我们先求出不包含模式串的个数,最后只需要相减就可以. 因为这道题目长度只要不超过n就可以,所以在构造矩阵的时候需要多加一列,该列值每行全设为1(这样最后一列的值就是对上一个矩阵每一行的和,完美计算了了各种长度

Hdu 2243 考研路茫茫——单词情结 (AC自己主动机+矩阵)

哎哟喂.中文题. . .不说题意了. 首先做过POJ 2778能够知道AC自己主动机是能够求出长度为L的串中不含病毒串的数量的. POJ 2778的大概思路就是先用全部给的病毒串建一个AC自己主动机.然后将AC自己主动机上全部非单词节点连一个边. 离散数学中有说道.假设矩阵A 中的 [i][j] 表示 i节点通过一条边能够走到j节点的方法数. 那么A*A这个矩阵的[i][j]就表示 i 节点到j 节点通过两条边能够走到j节点的方法数. 既然知道这种方法.我们就明白要求什么. ans= 26+26