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

题意:给出N个词根,求长不超过L的且至少包含一个上述词根的单词的个数。

数据范围:0<N<6,0<L<2^31

这题与上一题差不多,但比上题要繁琐的多,关键的区别在于"不超过L" 和"至少包含一个"

因为"至少包含一个",所以刚好是求上一题的反面;

因为"不超过L",所以要求的是矩阵幂和S = A + A2 + A3 + … + Ak

View Code
#include <stdio.h>

#include <string.h>

#include <queue>

#include <math.h>

using namespace std;



#define N 6

#define LEN 6

#define SIZE (N*LEN)

typedef unsigned __int64 LL;

int n,l,node;

int next[SIZE][26];

int fail[SIZE];

bool isend[SIZE];



LL mat[SIZE][SIZE];

LL ans[SIZE][SIZE];



void mat_add(LL a[][SIZE],LL b[][SIZE])

{

    for(int i=0;i<node;i++)

    {

        for(int j=0;j<node;j++) a[i][j]+=b[i][j];

    }

}

void mat_mul(LL a[][SIZE],LL b[][SIZE])

{

    LL tmp[SIZE][SIZE];

    for(int i=0;i<node;i++)

    {

        for(int j=0;j<node;j++)

        {

            tmp[i][j]=0;

            for(int k=0;k<node;k++) tmp[i][j]+=a[i][k]*b[k][j];

        }

    }

    memcpy(a,tmp,sizeof(mat));

}

void mat_pow(LL a[][SIZE],LL b[][SIZE],int k)

{

    memset(a,0,sizeof(mat));

    for(int i=0;i<node;i++) a[i][i]=1;



    LL mm[SIZE][SIZE];

    memcpy(mm,b,sizeof(mat));

    while(k)

    {

        if(k&1)

        {

            mat_mul(a,mm);

        }

        k>>=1;

        mat_mul(mm,mm);

    }

}

void pow_sum(LL a[][SIZE],LL b[][SIZE],int k)

{

    if(k==1)

    {

        memcpy(a,b,sizeof(mat));

        return;

    }

    LL c[SIZE][SIZE],d[SIZE][SIZE];

    pow_sum(a,b,k>>1);

    memcpy(d,a,sizeof(mat));

    mat_pow(c,b,k>>1);

    mat_mul(a,c);

    mat_add(a,d);

    if(k&1)

    {

        mat_mul(c,c);

        mat_mul(c,b);

        mat_add(a,c);

    }

}

void init()

{

    node=1;

    memset(next[0],0,sizeof(next[0]));

}

void add(int cur,int k)

{

    memset(next[node],0,sizeof(next[node]));

    isend[node]=0;

    next[cur][k]=node++;

}

void insert(char *s)

{

    int i,cur,k;

    for(i=cur=0;s[i];i++)

    {

        k=s[i]-'a';

        if(!next[cur][k])   add(cur,k);

        cur=next[cur][k];

    }

    isend[cur]=1;

}

void get_fail()

{

    queue<int>q;

    int cur,nxt,tmp;



    fail[0]=0;

    q.push(0);



    while(!q.empty())

    {

        cur=q.front(),q.pop();

        for(int k=0;k<26;k++)

        {

            nxt=next[cur][k];

            if(nxt)

            {

                if(!cur)    fail[nxt]=0;

                else

                {

                    for(tmp=fail[cur];tmp && !next[tmp][k];tmp=fail[tmp]);

                    fail[nxt]=next[tmp][k];

                }

                if(isend[fail[nxt]]) isend[nxt]=1;

                q.push(nxt);

            }

            else    next[cur][k]=next[fail[cur]][k];

        }

    }

}

void get_mat()

{

    memset(mat,0,sizeof(mat));

    for(int i=0;i<node;i++)

    {

        if(isend[i])    continue;

        for(int k=0;k<26;k++)

        {

            int j=next[i][k];

            if(!isend[j])   mat[i][j]++;

        }

    }

}

void solve()

{

    get_fail();

    get_mat();



    pow_sum(ans,mat,l);

    LL ret=0;

    for(int i=0;i<node;i++) ret-=ans[0][i];



    mat[0][0]=26;

    node=1;

    pow_sum(ans,mat,l);

    ret+=ans[0][0];

    printf("%I64u\n",ret);

}

int main()

{

    char s[LEN];

    while(~scanf("%d%d",&n,&l))

    {

        init();

        for(int i=0;i<n;i++)

        {

            scanf("%s",s);

            insert(s);

        }

        solve();

    }

    return 0;

}

 

你可能感兴趣的:(dp)