hdu 2243 考研路茫茫——单词情结(AC自动机+矩阵)

Problem Description
背单词,始终是复习英语的重要环节。在荒废了3年大学生涯后,Lele也终于要开始背单词了。
一天,Lele在某本单词书上看到了一个根据词根来背单词的方法。比如"ab",放在单词前一般表示"相反,变坏,离去"等。

于是Lele想,如果背了N个词根,那这些词根到底会不会在单词里出现呢。更确切的描述是:长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义。

比如一共有2个词根 aa 和 ab ,则可能存在104个长度不超过3的单词,分别为
(2个) aa,ab,
(26个)aaa,aab,aac...aaz,
(26个)aba,abb,abc...abz,
(25个)baa,caa,daa...zaa,
(25个)bab,cab,dab...zab。

这个只是很小的情况。而对于其他复杂点的情况,Lele实在是数不出来了,现在就请你帮帮他。
 

Input
本题目包含多组数据,请处理到文件结束。
每组数据占两行。
第一行有两个正整数N和L。(0<N<6,0<L<2^31)
第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。
 

Output
对于每组数据,请在一行里输出一共可能的单词数目。
由于结果可能非常巨大,你只需要输出单词总数模2^64的值。
 

Sample Input
  
  
2 3 aa ab 1 2 a
 

Sample Output
  
  
104 52
 

思路:用总的情况减去不可能的情况。

/*
   设f(n)=26+26^1+26^2+26^3+...+26^n,则f(n+1)=26*f(n)+26,
   所以|f(n+1)|  = |26 26| *|f(n)|
       |  1   |    | 0  1|  | 1  |
	|f(n)| = |26 26|^(n-1)  *|26|
	| 1  |   | 0  1|         | 1|
    在构造矩阵m[sz][sz]的时候,可以将矩阵变为m[sz+1][sz+1],m[][sz+1]全部为1,这样就可以计算
    矩阵幂的和
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef unsigned long long  ll;
const int kind=26;
const int maxn=40;
struct Matrix
{
	int r,c;
	ll a[maxn][maxn];
	void init(int r,int c)
	{
		this->r=r;
		this->c=c;
		memset(a,0,sizeof(a));
	}
	void clear()
	{
		memset(a,0,sizeof(a));
		r=c=0;
	}
	Matrix operator *(const Matrix &b)
	{
		Matrix temp;
		temp.init(r,b.c);
		for(int i=0;i<r;i++)
		{
			for(int j=0;j<b.c;j++)
			{
				for(int k=0;k<c;k++)
					temp.a[i][j]+=(a[i][k]*b.a[k][j]);
			}
		}
		return temp;
	}
};
Matrix matrix;
Matrix pow(Matrix &a,int n)
{
	Matrix ret ;
	ret.init(a.r,a.c);
    for(int i=0;i<a.r;i++)
        ret.a[i][i] = 1;
    Matrix tmp = a;
    while(n)
    {
        if(n&1)ret=ret*tmp;
        tmp=tmp*tmp;
        n>>=1;
    }
    return ret;
}
struct Aho_Corasick
{
	int ch[maxn][kind],sz;
	int val[maxn],fail[maxn],last[maxn];
	void init()
	{
		val[0]=0,sz=1;
		memset(ch[0],0,sizeof(ch[0]));
	}
	void insert(char *s,int v)
	{
		int u=0,len=strlen(s);
		for(int i=0;i<len;i++)
		{
			int id=s[i]-'a';
			if(!ch[u][id])
			{
				memset(ch[sz],0,sizeof(ch[sz]));
				val[sz]=0;
				ch[u][id]=sz++;
			}
			u=ch[u][id];
		}
		val[u]=v;
	}
	void getFail()
	{
		queue<int>q;
		fail[0]=0;
		for(int i=0;i<kind;i++)
		{
			int u=ch[0][i];
			if(u)
			{
				fail[u]=last[u]=0;
				q.push(u);
			}
		}
		while(!q.empty())
		{
			int r=q.front();
			q.pop();
			if(val[fail[r]]) val[r]=1;
			for(int i=0;i<kind;i++)
			{
				int u=ch[r][i];
				if(!u)
				{
					ch[r][i]=ch[fail[r]][i];
					continue;
				}
				q.push(u);
				int v=fail[r];
				while(v&&!ch[v][i]) v=fail[v];
				fail[u]=ch[v][i];
				last[u]=val[fail[u]]?fail[u]:fail[last[u]];
			}
		}
	}
	void buildMatrix()
	{
		matrix.init(sz+1,sz+1);
		for(int i=0;i<sz;i++)
		{
			for(int j=0;j<kind;j++)
			{
				int u=ch[i][j];
				if(!val[u])
				matrix.a[i][u]++;
			}
		}
		for(int i=0;i<sz+1;i++)
		matrix.a[i][sz]=1;
	}
}ac;
char P[20];
int main()
{
	Matrix mat;
	mat.init(2,2);
	mat.a[0][0]=mat.a[0][1]=26,mat.a[1][0]=0,mat.a[1][1]=1;
	int N;ll L;
	while(cin>>N>>L)
	{
		ac.init();
		ll ans1=0;
		Matrix res;
		res.init(2,1);
		res.a[0][0]=26,res.a[1][0]=1;
		res=pow(mat,L-1)*res;
		ans1=res.a[0][0];
		matrix.clear();
		for(int i=1;i<=N;i++)
		{
			scanf("%s",P);
			ac.insert(P,i);
		}
		ac.getFail();
		ac.buildMatrix();
		Matrix cur;
		ll ans2=0;
		matrix=pow(matrix,L);
		for(int i=0;i<matrix.r;i++)
		ans2+=matrix.a[0][i];
		ans2--;
		ll ans=ans1-ans2;
		cout<<ans<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值