hdu 2243 AC自动机 + 矩阵快速幂

//	hdu 2243 AC自动机 + 矩阵快速幂
//
//	题目大意:
//
//		给你一些短串,问在长度不超过k的任意串,包含至少一个这些短串的其中
//	一个.问这样的串有多少个.
//
//	解题思路:
//
//		首先, 包含和不包含是一种互斥关系,包含+不包含 = 全集u.全集的答案就是
//		26 ^ 1 + 26 ^ 2 + .... + 26 ^ k.不包含的比较好求.构建一个自动机,得到
//		一个转移矩阵A.表示状态i能到状态j的方法数.而这些状态中都是不包含所给的
//		串的.我们对这个矩阵进行k次幂,得到的矩阵的第一行的和,就是长度为k的不包
//		含串的的总数.我们要求的是A + A ^ 2 + A ^ 3 +.... + A ^ k.之后的部分就是
//		矩阵的再次构造.对于矩阵	| A  E |					
//
//					| 0  E | 对其求n次方,得到	
//
//					| A^n    1 + A + A^2 +...+A^(n-1)	|
//					| 0				E	|
//		对于这一题,我们增加一维构成全是1.即列全是1,行全是0除了处在最后一列上的除外.
//		对于26是一样的求法即	| 26 1 |					
//					| 0  1 | 对其求n次方,得到	
//
//					| 26^n	1 + 26 + 26 ^ 2 + ... + 26^(n-1)|
//					|	0			1	|
//		这样最后的答案就是全集 - 不包含.(矩阵写的搓,望各位体谅一二 (^ - ^)
//
//	感悟:
//
//		这道题,重点的思维还是在于补集的思想.将不好求的问题转化为好求的问题.自动机的
//		构建通过poj2778已经经历了一定程度的锤炼,不成问题.矩阵的构造也是问题不大,关键
//		是矩阵的n次方的求和.本来有一道裸地矩阵n次方求和,兴奋的学了学大牛的二分思想,
//		结果爆栈了,加了扩充栈以后,tle了...好吧,乖乖地再次构造一个矩阵,添加一维,然后
//		就过了,瞬间觉得矩阵太美妙了~~~~总的来说,这道题还是不错的,继续加油吧~~~FIGHTING
				

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

typedef unsigned long long ull;
const int MAX_N = 66;
const int SIGMA = 26;
//;const ull MOD = (ull)1 << 64;
struct Matrix {
	int r,c;
	ull mat[MAX_N][MAX_N];
}res;


struct Aho_Corasick{
	int ch[MAX_N][SIGMA];
	int f[MAX_N];
	int last[MAX_N];;
	bool val[MAX_N];
	int sz;

	void init(){
		memset(ch[0],-1,sizeof(ch[0]));
		f[0] = 0;
		last[0] = 0;
		sz = 1;
	}

	int idx(char c){
		return c - 'a';
	}

	void insert(char *s){
		int n = strlen(s);
		int u = 0;
		for (int i=0;i<n;i++){
			int c = idx(s[i]);
			if (ch[u][c]==-1){
				memset(ch[sz],-1,sizeof(ch[sz]));
				val[sz] = 0;
				ch[u][c] = sz++;
			}
			u = ch[u][c];
		}
		val[u] = true;
	}

	void getfail(){
		queue<int> que;
		for (int c=0;c<SIGMA;c++){
			int u = ch[0][c];
			if (u!=-1){
				que.push(u);
				f[u] = 0;
				last[u] = 0; 
			}
			else {
				ch[0][c] = 0;
			}
		}

		while(!que.empty()){
			int r = que.front();
			que.pop();

			if (val[f[r]])
				val[r] = true;

			for (int c = 0;c < SIGMA;c++){
				int u = ch[r][c];

				if (u==-1){
					ch[r][c] = ch[f[r]][c];
					continue;
				}
				
				que.push(u);

				int v = f[r];

				while(v && ch[v][c] == -1)
					v = f[v];

				f[u] = ch[v][c];

				last[u] = val[f[u]] ? f[u] : last[f[u]];
			}
		}

	}

	void get_Matrix(){
		memset(res.mat,0,sizeof(res.mat));
		for (int u = 0; u < sz; u ++){
			for (int c = 0; c < 26; c++){
				if (!val[u] && !val[ch[u][c]])
					res.mat[u][ch[u][c]]++;
			}
		}
		for (int i=0;i<sz+1;i++)
			res.mat[i][sz]=1;
		res.r = sz + 1;
		res.c = sz + 1;
		//print();
	}

}ac;


int n,l;

Matrix Multiply(Matrix a,Matrix b){
	Matrix ans;
	for (int i=0;i<a.r;i++)
		for (int j=0;j<b.c;j++){
			ans.mat[i][j] = 0;
			for (int k=0;k<a.c;k++)
				ans.mat[i][j] += a.mat[i][k] * b.mat[k][j];
		}
	ans.r = a.r;
	ans.c = b.c;
	return ans;
}

Matrix Add(Matrix a,Matrix b){
	for (int i=0;i<a.r;i++)
		for (int j=0;j<a.c;j++)
			a.mat[i][j] += b.mat[i][j];
	return a;
}

Matrix Power(Matrix a,int b){
	Matrix ans;
	ans.r = a.r;
	ans.c = a.c;
	memset(ans.mat,0,sizeof(ans.mat));
	for (int i=0;i<a.r;i++)
		ans.mat[i][i] = 1;

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

//Matrix sigma_Matrix(Matrix res,int k){    // 二分求 A + A^2 + A^3 + ... + A^n
//	if (k == 1)	return res;
//	Matrix B = Power(res,(k+1)/2);
//	Matrix C = sigma_Matrix(res,k/2);
//	if (k&1){
//		return Add(res,Multiply(Add(res,B),C));
//	}else {
//		return Multiply(Add(Power(res,0),B),C);
//	}
//}

ull Power_Int(ull a,int b){
	ull res = 1;
	while(b){
		if (b & 1)
			res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}

//ull sigma_Int(ull a,int k){	// 二分 求整数 x + x^2 + x^3 + ... + x^n
//	if (k==1)	return a;
//	ull b = Power_Int(a,(k+1)/2);
//	ull c = sigma_Int(a,k/2);
//
//	//printf("b = %llu c = %llu k = %d\n",b,c,k);
//	if (k & 1){
//		return a + (a + b) * c;
//	}else {
//		return (1 + b) * c;
//	}
//}
//

//void print(){
//	for (int i=0;i<ac.sz;i++){
//		for (int j=0;j<ac.sz;j++)
//			printf("%llu ",res.mat[i][j]);
//		puts("");
//	}
//}

void input(){
	char s[20];
	ac.init();
	for (int i=0;i<n;i++){
		scanf("%s",s);
		ac.insert(s);
	}
	ac.getfail();
	ac.get_Matrix();
//	print();	
	//res = sigma_Matrix(res,l);
	res = Power(res,l);
	ull ans = 0;
	for (int i=0;i<ac.sz+1;i++){
		ans += res.mat[0][i];
	}
	ans--;
	res.r = 2;
	res.c = 2;
	res.mat[0][0] = 26;
	res.mat[0][1] = res.mat[1][1] = 1;
	res.mat[1][0] = 0;
	res = Power(res,l);
	ull sum = res.mat[0][1] + res.mat[0][0]-1;
	
	//printf("ans = %llu sum = %llu l = %d\n",ans,sum,l);
	printf("%llu\n",sum-ans);
}

//void init(){
//	for (int i=0;i<MAX_N;i++)
//		x.mat[i][i] = 1;
//}

int main(){
	//init();
	//freopen("1.txt","r",stdin);
	while(scanf("%d%d",&n,&l)!=EOF){
		input();
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值