[AC自动机] HDU2243 考研路漫漫--单词情结

[(http://acm.hdu.edu.cn/showproblem.php?pid=2243)]

我觉得很不错的一道AC自动机

首先把题目转换为求不含词根的数量
考虑构造一个矩阵mat[i][j]表示状态i到状态j走一步的数量
其中i和j必须要是合法状态
设s1=26^1+26^2+…+26^L,s2=mat^1+mat^2+…+mat^L
则答案等于s1-s2

mat可以由ac自动机辅助构造

注意s2的求法 可以看这个blog
[(http://www.cnblogs.com/ftae/p/7351524.html)]

#include<cstdio>
#include<cstring>
using namespace std;

typedef unsigned long long ULL;

int N;
char S[6];
int Tot,Son[30][26];
bool Can[30];
int Fail[30],Q[30];
ULL L,Mat[65][65],Tmp[65][65],Ans[65][65],Base[65][65];

void Insert(){
    int i,u=0,l=strlen(S+1),c;
    for(i=1;i<=l;i++){
        c=S[i]-'a';
        if(!Son[u][c])
            Son[u][c]=++Tot;
        u=Son[u][c];
    }
    Can[u]=false;
}

void Init(){
    int i;
    Tot=0;
    memset(Son,0,sizeof(Son));
    memset(Can,true,sizeof(Can));
    for(i=1;i<=N;i++){
        scanf("%s",S+1);
        Insert();
    }
}

void Get_Fail(){
    int i,u,v,h=0,t=1,tmp;
    memset(Fail,-1,sizeof(Fail));
    Q[h]=0;
    while(h<t){
        u=Q[h++];
        for(i=0;i<26;i++)
            if(v=Son[u][i]){
                tmp=Fail[u];
                while(tmp!=-1&&!Son[tmp][i])
                    tmp=Fail[tmp];
                Fail[v]=tmp==-1?0:Son[tmp][i];
                Can[v]&=Can[Fail[v]];
                Q[t++]=v;
            }
            else Son[u][i]=Fail[u]==-1?0:Son[Fail[u]][i];
    }
}

void Get_Matrix(){
    int i,j,tmp;
    memset(Mat,0,sizeof(Mat));
    for(i=0;i<=Tot;i++)
        if(Can[i])
            for(j=0;j<26;j++)
                if(Can[tmp=Son[i][j]])
                    Mat[i][tmp]++;
    for(i=0;i<=Tot;i++)
            Mat[i][i+Tot+1]=1;
    for(i=Tot+1;i<=2*Tot+1;i++)
            Mat[i][i]=1;
}

void Mul(ULL a[65][65],ULL b[65][65],int l){
    int i,j,k;
    memset(Tmp,0,sizeof(Tmp));
    for(i=0;i<l;i++)
        for(j=0;j<l;j++)
            for(k=0;k<l;k++)
                Tmp[i][j]+=a[i][k]*b[k][j];
    memcpy(a,Tmp,sizeof(Tmp));
}

void Pow(ULL base[65][65],ULL n,int l){
    int i;
    memset(Ans,0,sizeof(Ans));
    for(i=0;i<l;i++)
        Ans[i][i]=1;
    while(n){
        if(n&1)
            Mul(Ans,base,l);
        Mul(base,base,l);
        n>>=1;
    }
    memcpy(base,Ans,sizeof(Ans));
}

void Work(){
    int i;
    ULL sum=0;
    Pow(Mat,L+1,2*Tot+2);
    for(i=Tot+1;i<=2*Tot+1;i++)
        sum+=Mat[0][i];
    Base[0][0]=Base[0][1]=26,Base[1][1]=1;
    Pow(Base,L-1,2);
    printf("%llu\n",(Base[0][1]+Base[1][1])*26-sum+1);
}

int main(){
    while(~scanf("%d%llu",&N,&L)){
        Init();
        Get_Fail();
        Get_Matrix();
        Work();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值