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;
}

 

posted @ 2012-08-08 20:25  BeatLJ  阅读(299)  评论(0编辑  收藏  举报