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();
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-06 21:45:04

hdu 2243 AC自动机 + 矩阵快速幂的相关文章

POJ - 2778 ~ HDU - 2243 AC自动机+矩阵快速幂

这两题属于AC自动机的第二种套路通过矩阵快速幂求方案数. 题意:给m个病毒字符串,问长度为n的DNA片段有多少种没有包含病毒串的. 根据AC自动机的tire图,我们可以获得一个可达矩阵. 关于这题的tire图详解可以点击这里,往下面翻,这个博主的图对于tire图讲的非常详细. 知道了什么是tire图,理解了tire图后,后面的AC自动机的题目才能写. AC自动机的灵魂应该就是tire图 然后问题就变成了,得到了一个可达矩阵后,如何求方案数呢? 这个n = 2000000000 这咋办呢? 给定一

考研路茫茫――单词情结 HDU - 2243(ac自动机 + 矩阵快速幂)

考研路茫茫--单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6853    Accepted Submission(s): 2383 Problem Description 背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了.一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如

HDU 2243 考研路茫茫――单词情结 (AC自动机 + 矩阵快速幂)

题目链接:考研路茫茫――单词情结 做本题前,个人建议先做一下POJ 2778 http://blog.csdn.net/u013446688/article/details/47378255 POJ2778 是求长度为n,不包含模式串的字符串个数. 而本题是求长度为n,包含模式串的字符串个数.直接用字符串总数减去不包含模式串的字符串个数即为所求. 同样是AC自动机 + 矩阵快速幂.但是还是有所不同的. 因为对2^64取模,所以定义数据类型为unsigned long long就可以了,这样就实现

poj 2778 AC自动机 + 矩阵快速幂

// poj 2778 AC自动机 + 矩阵快速幂 // // 题目链接: // // http://poj.org/problem?id=2778 // // 解题思路: // // 建立AC自动机,确定状态之间的关系,构造出,走一步 // 能到达的状态矩阵,然后进行n次乘法,就可以得到状态间 // 走n步的方法数. // 精髓: // 1):这个ac自动机有一些特别,根节点是为空串,然而 // 每走一步的时候,如果没法走了,这时候,不一定是回到根 // 节点,因为有可能单个的字符时病毒,这样

POJ POJ 2778 DNA Sequence AC自动机 + 矩阵快速幂

首先建立Trie和失败指针,然后你会发现对于每个节点 i 匹配AGCT时只有以下几种情况: i 节点有关于当前字符的儿子节点 j 且安全,则i 到 j找到一条长度为 1的路. i 节点有关于当前字符的儿子节点 j 且 不安全,则i 到 j没有路. i 节点没有关于当前字符的儿子节点 但是能通过失败指针找到一个安全的节点j,那么 i 到 j 找到一条长度为1的路. 关于节点安全的定义: 当前节点不是末节点且当前节点由失败指针指回跟节点的路径上不存在不安全节点,那么这个节点就是安全节点. 然后问题就

hdu 2243 考研路茫茫——单词情结 ac自动机+矩阵快速幂

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243 题意:给定N(1<= N < 6)个长度不超过5的词根,问长度不超过L(L <231)的单词中至少含有一个词根的单词个数:结果mod 264. 基础:poj 2778DNA 序列求的是给定长度不含模式串的合法串的个数:串长度相当,都到了int上界了: 1.mod 264直接使用unsigned long long自然溢出即可:说的有些含蓄..并且也容易想到是直接使用内置类型,要不然高精度的

HDU 2243 考研路茫茫――单词情结 ——(AC自动机+矩阵快速幂)

和前几天做的AC自动机类似. 思路简单但是代码200余行.. 假设solve_sub(i)表示长度为i的不含危险单词的总数. 最终答案为用总数(26^1+26^2+...+26^n)减去(solve_sub(1)+solve(2)+...+solve_sub(n)).前者构造f[i]=f[i-1]*26+26然后矩阵快速幂即可(当然也可以分治的方法).后者即构造出dp矩阵p,然后计算(p^1+p^2+...+p^n),对其分治即可. 代码如下: 1 #include <stdio.h> 2 #

HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)

背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般表示"相反,变坏,离去"等. 于是Lele想,如果背了N个词根,那这些词根到底会不会在单词里出现呢.更确切的描述是:长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义. 比如一共有2个词根 aa 和 ab ,则可能存在104个长度不

poj2778DNA Sequence (AC自动机+矩阵快速幂)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud DNA Sequence Time Limit: 1000MS   Memory Limit: 65536K Description It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DN