hdu 2243 考研路茫茫——单词情结

hdoj 2243

 

  不仅仅是考研路茫茫 A此题的过程也是路茫茫 终于到最后柳暗花明了

  A这个题用了3天 或者说用了1个月(因为1个月前就学了KMP和AC自动机)

  虽然上个月那节课学了KMP匹配算法+字典树+AC自动机。 当时就听得一头雾水 特别是KMP那个失败指针(或者说成是next[] fail[]...)

  有那么点点的抽象 听完之后的水平当然是非常烂 只能做一些模板题或者拿模板做做题 稍微变一下的问题就不会了 关键还是对算法理解不深 KMP都不会更不说AC自动机了

  那节课后过了几天 遇到一场吉林大学主办的多校联赛 其中有一个题http://acm.jlu.edu.cn/joj/showproblem.php?pid=2754

旁边几个队过得很快 我们队题量少的弱点瞬间体现了出来 AC自动机不会啊(其实当时根本没意识到是AC自动机能解决的...鄙视)

 然后此题终于A了 推了近4个小时的公式然后用矩阵加速过了 赛后听同学讨论AC自动机...什么 有AC自动机?

 

  那个题也是因为当时推出公式 后面就没管了

 

  最近重新粗浅地学了下字符串匹配 理解比以前当然要深些了 至少AC自动机构造了解了点嘛

 

   说说这道题吧

   这个题意是给出一个 n 和一个l

   然后给出n个单词 求长度为l的小写字母串中至少包含n个单词中1个词的串的个数(好像有点点绕口)

   地址在这里:http://acm.hdu.edu.cn/showproblem.php?pid=2243

   "至少"这中词汇很敏感 可以从对立角度来想想.

   就是先求出所有串的个数 然后减去一个单词都不包含的个数咯

   所有串的个数就是26^1+26^2+...26^l次方

   这个可以用一个函数写 这个多项式可以二分求解

   我代码里是Sum(x,n)函数 求x^1+x^2+...x^n

   至于求一个单词都不包含的串的个数

   这个就可以用到AC自动机了 AC自动机构建矩阵!

 

   我当时很纳闷 AC自动机不是解决字符串匹配的吗?(所以说不能老用模板A题,模板就是拿来匹配的)

   然后就去找相关AC自动机构建矩阵的资料http://hi.baidu.com/%D2%D5%C1%D6010/blog/item/6db06ccf0a3b440993457e7b.html

   这篇文章写得很好 我也很感谢他 求一个串中不包含单词的办法

   看了上面那个 理解了很久 主要就是它那个矩阵 怎么ans+=matrix[0][i]就是最后的答案 而且关于矩阵的传递闭包没个概念

   然后就经过了2天。。。(时间主要花在理解这个矩阵上面了)

   尼玛的我终于搞懂了 我们构造矩阵的方式不一样 我的矩阵是转置后乘右边的一个nx1向量 他是不转置左边乘一个1xn向量

   求一个固定长度l不包含任何单词的方法会了

   只要理解了那个矩阵 hdu2243这个题就好办了 因为这题就加了一个求所有长度不包含然和单词的串

   只需要包矩阵多加一行一列就行了

   这个可以自己去理解构造

   然后这题一直timelimit

   我无语了 我试了很大的数据 20亿都是一瞬间出结果 再看人家交的代码都是15MS  这题也不卡时间啊

   然后又纠结了一下午 然后就发现这题有个数据就是2^31-1

   因为矩阵我自己加了一行一列 然后矩阵求冥就是求(l+1)次方

   l我用的int  然后l+1  然后就溢出啦 然后死循环。。。

 

   我怎么发现这个的?

 

   因为2^31-2可以出数据 2^31-1 就不行了啊!!!!尼玛啊

 

   改过就A了!

   这个时间花长了 不过理解也深了 与其粗浅地学很多算法 不如深入地学一两个算法

 

   最近不喜欢用Vc了 老死机 太烂的BUG啊 不过断点调试又要用它。。。

   

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
#define KIND 26
#define MAXN 33
#define FF(i,n) for(i=0;i<n;i++)
typedef unsigned __int64 ULL;

struct IM
{
    ULL el[MAXN][MAXN];
}sgl,bas;
int N;

IM operator * (IM &x,IM &y)
{
    IM r;
    int i,j,k;
    FF(i,N)
        FF(j,N)
        r.el[i][j]=0;
    FF(i,N)
        FF(k,N)
        if(x.el[i][k])
            FF(j,N)
            if(y.el[k][j])
                r.el[i][j]+=x.el[i][k] * y.el[k][j];
    return r;
}

IM operator ^ (IM x,ULL m)
{
    IM r=sgl;
    for(;m;m>>=1)
    {
        if(m&1)
            r=r*x;
        x=x*x;
    }
    return r;
}

struct Node
{
    int fail;
    bool end;
    int next[KIND];
    void init()
    {
        fail=-1;
        end=0;
        memset(next,-1,sizeof(next));
    }
}node[MAXN];

int q[MAXN<<1];
int head,tail;
int sz;

void init()
{
    sz=0;
    node[0].init();
}

void Insert(char *str)
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        if(node[p].end)
            break;
        int index=str[i]-'a';
        if(node[p].next[index]==-1)
        {
            node[p].next[index]=++sz;
            node[sz].init();
        }
        p=node[p].next[index];
    }
    node[p].end=1;
}

void Build_AC()
{
    int temp;
    int p=0;

    head=tail=0;
    q[tail++]=p;
    while(head<tail)
    {
        temp=q[head++];
        for(int i=0;i<KIND;i++)
        {
            p=node[temp].next[i];
            if(p!=-1)
            {
                if(!temp)
                    node[p].fail=0;
                else
                {
                    node[p].fail=node[node[temp].fail].next[i];
                    if(node[node[p].fail].end)
                        node[p].end=1;
                }
                q[tail++]=p;
            }
            else
            {
                if(!temp)
                    node[temp].next[i]=0;
                else
                    node[temp].next[i]=node[node[temp].fail].next[i];
            }
        }
    }
}

ULL power(ULL a,int n)
{
    ULL ret;
    for(ret=1;n;n>>=1)
    {
        if(n & 1)
            ret*=a;
        a*=a;
    }
    return ret;
}

ULL Sum(ULL x,int n)
{
	if(n==1)
		return x;
    if(n & 1)
        return Sum(x,n>>1)*(power(x,(n>>1)+1)+1)+power(x,(n>>1)+1);
    return Sum(x,n>>1)*(power(x,n>>1)+1);
}

int main()
{
    int n;
	ULL l;
    char words[11];

    while(~scanf("%d %I64u",&n,&l))
    {
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",words);
            Insert(words);
        }

        Build_AC();
        sz+=2;
        N=sz;

        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
            {
                bas.el[i][j]=0;
                sgl.el[i][j]=(i==j);
            }
        for(int i=0;i<sz-1;i++)
        {
            if(!node[i].end)
            for(int j=0;j<KIND;j++)
                if(!node[node[i].next[j]].end)
                    bas.el[node[i].next[j]][i]++;
        }
        for(int i=0;i<N;i++)
            bas.el[N-1][i]=1;

		/*
		for(i=0;i<N;i++)
		{
			for(int j=0;j<N;j++)
				printf("%I64u ",bas.el[i][j]);
			puts("");
		}
		*/

        IM mat=bas^(l+1);
        ULL s=mat.el[N-1][0]-1;
        ULL top=Sum(26,l);
		ULL ans=top-s;
		if(ans<0)
			ans=ans+((ULL)1<<63)+((ULL)1<<63);
        printf("%I64u\n",ans);
    }
    return 0;
}


 

 

    

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值