HDU 2243 考研路茫茫——单词情结(自动机)

本文详细解析了ACM竞赛中的一种字符串匹配算法,通过构建矩阵和使用快速幂运算,解决了求解特定条件下字符串数量的问题。文章深入探讨了算法的实现细节,包括矩阵构建、快速幂运算以及如何优化计算过程。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243

题意:给出m个串,问有多少不大于n的串至少包含m个串中的一个?

思路:首先求出不包含m个串的长度不大于n的串的个数,也就是对应矩阵A:S=A^1+A^2+……A^n。然后就是26+26^2+26^3+……+26^n-S。





const int mod=100000;
const int N=105;


struct node
{
    int next[26],fail,flag;

    void init()
    {
        clr(next,0);
        fail=-1;
        flag=0;
    }
};

node a[N];
int e,n,m;
char s[N];



void insert(char s[])
{
    int i,k,p=0;
    for(i=0;s[i];i++)
    {
        k=s[i]-'a';
        if(a[p].next[k]==0)
        {
            a[e].init();
            a[p].next[k]=e++;
        }
        p=a[p].next[k];
    }
    a[p].flag=1;
}

queue<int> Q;

void build()
{
    Q.push(0);
    int i,j,k,p,q;
    while(!Q.empty())
    {
        k=Q.front();
        Q.pop();
        for(i=0;i<26;i++)
        {
            if(a[k].next[i])
            {
                p=a[k].next[i];
                q=a[k].fail;
                while(q!=-1&&!a[q].next[i]) q=a[q].fail;
                if(q==-1) a[p].fail=0;
                else
                {
                    a[p].fail=a[q].next[i];
                    a[p].flag|=a[a[p].fail].flag;
                }
                Q.push(p);
            }
            else
            {
                q=a[k].fail;
                while(q!=-1&&!a[q].next[i]) q=a[q].fail;
                if(q==-1) a[k].next[i]=0;
                else a[k].next[i]=a[q].next[i];
            }
        }
    }
}


u64 b[N][N],d[N][N];

void mul(u64 a[][N],u64 b[][N])
{
    int i,j,k;
    u64 c[N][N]={0};
    FOR0(k,e) FOR0(i,e) FOR0(j,e)
    {
        c[i][j]+=a[i][k]*b[k][j];
    }
    FOR0(i,e) FOR0(j,e) b[i][j]=c[i][j];
}

u64 getPow(u64 a,u64 b)
{
    u64 ans=1;
    while(b)
    {
        if(b&1) ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}

void copyMatrix(u64 A[][N],u64 B[][N])
{
    int i,j;
    FOR0(i,e) FOR0(j,e) B[i][j]=A[i][j];
}

u64 getSum(u64 a,u64 n)
{
    if(n==0) return 1;
    if(n&1) return getSum(a,n>>1)*(1+getPow(a,n/2+1));
    else return getPow(a,n)+getSum(a,n/2-1)*(1+getPow(a,n/2));
}


void initMatrix(u64 A[][N])
{
    int i,j;
    FOR0(i,e) FOR0(j,e) A[i][j]=0;
    FOR0(i,e) A[i][i]=1;
}

void getPOW(u64 A[][N],u64 B[][N],int n)
{
    initMatrix(B);
    u64 C[N][N];
    copyMatrix(A,C);
    while(n)
    {
        if(n&1) mul(C,B);
        mul(C,C);
        n>>=1;
    }
}



void addMatrix(u64 A[][N],u64 B[][N])
{
    int i,j;
    FOR0(i,e) FOR0(j,e) B[i][j]+=A[i][j];
}

void print(u64 A[][N])
{
    int i,j;
    FOR0(i,e)
    {
        FOR0(j,e) printf("%I64u ",A[i][j]);
        puts("");
    }
    puts("");
}


u64 C[N][N],D[N][N],E[N][N];

void getSum(u64 A[][N],u64 B[][N],int n)
{
    if(n==0)
    {
        initMatrix(B);
        return;
    }


    if(n&1)
    {
        getSum(A,B,n/2);
        initMatrix(C);
        getPOW(A,D,n/2+1);
        addMatrix(C,D);
        mul(D,B);
    }
    else
    {
        getSum(A,B,n/2-1);
        getPOW(A,C,n);
        initMatrix(D);
        getPOW(A,E,n/2);
        addMatrix(D,E);
        mul(E,B);
        addMatrix(C,B);
    }
}



int main()
{
    while(scanf("%d%d",&m,&n)!=-1)
    {
        a[0].init();e=1;
        int i,j,k;
        FOR0(i,m) RD(s),insert(s);
        build();
        clr(b,0);
        FOR0(i,e) if(!a[i].flag) FOR0(j,26)
        {
            k=a[i].next[j];
            if(!a[k].flag) b[i][k]++;
        }
        u64 sum=getSum(26,n),x=((u64)1)<<63;;
        getSum(b,d,n);
        FOR0(i,e) if(!a[i].flag) sum-=d[0][i];
        printf("%I64u\n",sum);
    }
    return 0;
}

  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值