HDU2243-AC自动机+矩阵快速幂

HDU2243

题目描述

长度不超过 L L L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义。

题解

先考虑直接dp转移,但是考虑到 n n n的大小,显然会T掉。

考虑矩阵乘法, a n s = 总 方 案 数 − 不 含 词 根 的 单 词 ans=总方案数-不含词根的单词 ans=
首先前半部分是一个幂和的形式:
A n = 2 6 1 + . . . + 2 6 n S n = A n + 1 = 2 6 0 + 2 6 1 + . . . + 2 6 n S n + 1 = 26 S n + 1 A_n=26^1+...+26^n\\ S_n=A_n+1=26^0+26^1+...+26^n\\ S_{n+1}=26S_n+1 An=261+...+26nSn=An+1=260+261+...+26nSn+1=26Sn+1
显然可以用矩形快速幂解决

后半部分完全相同于POJ2778,就不多赘述。

代码

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
int tr[50][26],fail[50],val[50],cnt,n,m;
ull sum;
char s[50];
struct Matrix{
	ull a[50][50];
	Matrix(){memset(a,0,sizeof(a));}
	Matrix operator*(const Matrix &b)const{
		Matrix res;
		for(int i=0;i<=cnt+1;i++)
			for(int j=0;j<=cnt+1;j++)
				for(int k=0;k<=cnt+1;k++)
					res.a[i][j]=(res.a[i][j]+a[i][k]*b.a[k][j]);
		return res;
	}
}ans,base; 
void clear(){
	cnt=0;
	memset(tr,0,sizeof(tr));
	memset(fail,0,sizeof(fail));
	memset(val,0,sizeof(val));
}
void init1(){
	for(int i=0;i<=49;i++)
		for(int j=0;j<=49;j++)
			base.a[i][j]=ans.a[i][j]=0;
	for(int i=0;i<=cnt;i++){
		for(int j=0;j<26;j++){
			int x=tr[i][j],y=val[x];
			if(!y&&!val[i]) base.a[x][i]++;
		}
	}for(int i=0;i<=cnt+1;i++) base.a[cnt+1][i]=1;
	ans.a[0][0]=1;
}
void init2(){
	for(int i=0;i<=49;i++)
		for(int j=0;j<=49;j++)
			base.a[i][j]=ans.a[i][j]=0;
	ans.a[0][0]=ans.a[1][0]=1;
	base.a[0][0]=26,base.a[0][1]=base.a[1][1]=1;
}
void ksm(long long b){
	while(b){
		if(b&1) ans=base*ans;
		base=base*base;
		b>>=1;
	}
}
void DP(){
	sum=0;
	init1();ksm((long long)m);
	for(int i=0;i<=cnt+1;i++) sum+=ans.a[i][0];
	init2();ksm((long long)m);
	//cout<<sum<<endl;
	cout<<ans.a[0][0]-sum<<endl;
}
void build(){
	int u=0,len=strlen(s+1);
	for(int i=1;i<=len;i++){
		int c=s[i]-'a';
		if(!tr[u][c]) tr[u][c]=++cnt;
		u=tr[u][c];
	}val[u]=1;
} 
void getfail(){
	queue<int>q;
	for(int i=0;i<26;i++)
		if(tr[0][i]) q.push(tr[0][i]);
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=0;i<26;i++){
			if(!tr[u][i]) tr[u][i]=tr[fail[u]][i];
			else{
				int x=tr[u][i],y=fail[u];
				while(y&&!tr[y][i]) y=fail[y];
				fail[x]=tr[y][i],val[x]|=val[fail[x]];
			}
		}
	} 
}
signed main(){
	while(~scanf("%d%d",&n,&m)){
		clear();
		for(int i=1;i<=n;i++)
			scanf("%s",s+1),build();
		getfail();DP(); 
	}return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值