hdu 2243 是poj2778的升级版

#pragma comment(linker, "/STACK:1024000000,1024000000")//防止爆栈用的  
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <vector>
#include <queue>     
#define LL unsigned __int64   //定义成这样就不需要考虑%mod(mod为2^64)   
using namespace std;
int k;
//注意这里和poj2778不同,它要我们计算的是从1-n长度的单词数(要用到两次两分),而poj那道只要我们计算长度为n的单词数
//注意ac自动机fail指针和kmp中的next类似,fail指针所跳到的地方是它本身最大的后缀和跳到的地方的最大前缀
struct node   //定义静态字典树
{
    int isword;
    int next[26];
    int fail;   //定义失败指针
    void init()
    {
        for(int t=0; t<26; ++t)
        {
            next[t]=0;
        }
        fail=-1;
        isword=0;
    }
}a[35];

int newnode()
{
    a[k].init();
    return k;
}
void insert(char *p)   //建树
{
    int t, j, g=0, h;   //g=0表示根
    j=strlen(p);
    for(t=0; t<j; ++t)
    {
        h=p[t]-'a';   //将字符转化为相应的数字
        if(!a[g].next[h])
        {
            a[g].next[h]=newnode();
			k++;
        }
        g=a[g].next[h];
    }
    a[g].isword=1;   //结尾标记
    return ;
}

void acAutomation()   //构建失败指针fail
{
    int t;
    queue<int> q;
    q.push(0);
    while(!q.empty())
    {
        int s=q.front();
        q.pop();
        for(t=0; t<26; ++t)
        {
            if(a[s].next[t]==0)
            {
                if(s==0)   
                    a[s].next[t]=0;
                else a[s].next[t]=a[a[s].fail].next[t];  //不是根节点的话就是s节点的fail所指的节点的next[t]位置             
            }
            else
            {
                if(s==0)
                {
                    a[a[s].next[t]].fail=0;
                }
                else 
                {
                    int temp=a[s].fail;
                    while(temp!=-1)
                    {
                        if(a[temp].next[t])
                        {
                            a[a[s].next[t]].fail=a[temp].next[t];
                            a[a[s].next[t]].isword|=a[a[temp].next[t]].isword;  //a[s].next[t]的最大后缀就是a[a[temp].fail].next[t]前缀,如果a[a[temp].fail].next[t]有标记,则a[s].next[t]也要标上
                            break;
                        }
                        temp=a[temp].fail;
                    }
                    if(temp==-1)   //到根节点也找不到对应的next
                    {
                        a[a[s].next[t]].fail=0;
                    }
                }
                q.push(a[s].next[t]);
            }
        }
    }
    return ;
}


struct matrix
{
    LL b[35][35];   //防止溢出
    void init()
    {
		int t, j;
        for(j=0; j<35; ++j)
        {
            for(t=0; t<35; ++t)
                b[j][t]=0;
        }
    }
	matrix operator*(matrix a1)   //重载可以减少内存,但速度会慢点
	{
		matrix q;
		q.init();
		int j, t, g;
		for(j=0; j<k; ++j)
		{
			for(t=0; t<k; ++t)
			{
				for(g=0; g<k; ++g)
				{
					q.b[j][t]+=b[j][g]*a1.b[g][t];
				}
			}
		}
		return q;
	}
	matrix operator+(matrix a1)
	{
		matrix q;
		int j, t;
		for(j=0; j<k; ++j)
		{
			for(t=0; t<k; ++t)
				q.b[j][t]=b[j][t]+a1.b[j][t];
		}
		return q;
	}
}p;

matrix f(int x)   //矩阵快速幂,求出A^n
{
    matrix q, s=p;
    int j, t;
    for(j=0; j<k; ++j)
    {
        for(t=0; t<k; ++t)
        {
            if(j==t)
                q.b[j][t]=1;
            else q.b[j][t]=0;
        }
    }
    while(x)
    {
        if(x&1)
            q=q*s;
        x=x>>1;
        s=s*s;
    }
    return q;
}

matrix f1(int x)   //二分计算矩阵 A^1 + A^2 + A^3 + A^4 + A^5 + ...A^x  --------构建无词根的矩阵
{
    if(x==1)return p;
    matrix temp=f1(x/2);
    matrix s;
    if(x&1)
    {
        s=temp+temp*f(x/2);
        s=s+f(x);
    }
    else s=temp+temp*f(x/2);
    return s;
}

LL f2(int x)   //二分计算26^x
{
	if(x==1)return 26;
	LL s=f2(x/2);
	s=s*s;
	if(x&1)s*=26;
	return s;
}

LL fsum(int x)   //计算长度不超过x的单词总数  26^1 + 26^2 + 26^3 + 26^4 + 26^5 + ...26^x
{
	if(x==1)return 26;
	LL q=fsum(x/2);
	LL s;
	s=q+f2(x/2)*q;
	if(x&1)
		s+=f2(x);
	return s;
}

int main()
{
    int n, m, t, j, g;
    char q[10];
	while(scanf("%d%d", &m, &n)!=EOF)
	{
        a[0].init();
        k=1;
        for(t=0; t<m; ++t)
        {
            scanf("%s", q);
            insert(q);
        }
        acAutomation();
		p.init();
		for(j=0; j<k; ++j)   //构建无词根的矩阵
		{
			if(a[j].isword)continue;
			for(t=0; t<26; ++t)
			{
				g=a[j].next[t];
				if(a[g].isword)continue;
				p.b[j][g]++;
			}
		}
		matrix result=f1(n);  //矩阵快速幂
		LL ans, sum;
		for(t=0, ans=0; t<k; ++t)  //统计没有词根的数目
		{
			ans+=result.b[0][t];
		}
		sum=fsum(n);   //计算全部单词的数目
		ans=sum-ans;
		printf("%I64u\n", ans);  //注意这里输出的是%I64u
	}
	return 0;
}
				

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值