AC自动机+矩阵+二分

自动机+矩阵快速幂+二分

题目:考研路茫茫——单词情结

题目大意:给出n个串,求至少包含一个串的长度不操过L的串的个数

思路:首先就是思路转化,自动机一般求的是不包含危险串的字串的个数,因此我们就反过来求,先求所有可能的串26+26^2+26^3…+26^L,然后不可能包含的串的个数,即长度为1,2,。。。,L的不包含给定的n个串的个数。然后用全部的减去这个,就是我们要的答案。

我们知道x^y可用快速幂求,那么幂的前n项和呢?

x^1+x^2+x^3+⋯+x^L=(x^1+x^2+⋯+x^(L/2) )*x^((L+1)/2) (当l是偶数时)

奇数时再加个中间项x^((L+1)/2)便可

这样就可以二分求解幂的前n项和了

对于一个图来说,他的邻接矩阵是A,离散数学告诉我们A^1是两点件距离为1的路径条数,A^2是两点间距离为2的路径条数,那么长度不超过L的距离和便是

A^1+A^2+…+A^L 这个也可用上面的二分进行求解

 

提交情况:TLE数次,原因长度应该是long long类型

体会 :好题


/*
	题目 :http://acm.hdu.edu.cn/showproblem.php?pid=2243
	考研路茫茫——单词情结
	给出n个串, 求至少包含一个串的长度不操过L的串的个数。利用图的n次幂表示两点间路径长度是n的个数。
	再利用二分求幂的前n项和
 */

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define maxn 7
#define maxnl 6
#define maxstate 30
#define size 26
typedef unsigned long long u64;

struct matrix{
	u64 mat[maxstate][maxstate];
	int n, m;
	matrix(){n = m = maxstate; }
	void init(int _n, int _m){ n = _n, m = _m; }
	/* 构造零矩阵 */
	void setZero(){ memset(mat, 0, sizeof(mat)); }
	/* 构造单位矩阵 */
	void setOne(){
		for(int i = 0; i < n; ++ i){
			for(int j = 0; j < m; ++ j){
				mat[i][j] = (i == j);
			}
		}
	}
} matx;

/* 矩阵加法 */
matrix operator + (const matrix &A, const matrix &B){
	matrix temp;
	temp.init(A.n, A.m);
	for(int i = 0; i < A.n; ++ i){
		for(int j = 0; j < A.m; ++ j){
			temp.mat[i][j] = A.mat[i][j] + B.mat[i][j];
		}
	}
	return temp;
}
/* 矩阵乘法 */
matrix operator * (const matrix &A, const matrix &B){
	matrix temp;
	temp.init(A.n, B.m);  
	temp.setZero();
	for(int k = 0; k < A.m; ++ k){
		for(int i = 0; i < A.n; ++ i){
			if(A.mat[i][k])
				for(int j = 0; j < B.m; ++ j){
					temp.mat[i][j] += A.mat[i][k] * B.mat[k][j];
				}
		}
	}
	return temp;
}
/* 矩阵幂 */
matrix operator ^ (matrix A, u64 k){
	matrix ans;
	ans.init(A.n, A.m);
	ans.setOne();
	while(k){
		if(k & 1) ans = ans * A;
		A = A * A;
		k >>= 1;
	}
	return ans;
}

struct automatonNode{
	bool real;
	int id;
	automatonNode* next[size],* fail;
	void clear(){
		memset(next, NULL, sizeof(next));
		real = 0;
		id = -1;
	}
};

struct automaton{
	automatonNode* root, * link,** queue;
	int head, tail, ad, tot;
	automaton(){ 
		link = new automatonNode[maxn * maxnl];
		queue = new automatonNode*[maxn * maxnl];
	}
	~automaton(){
		delete[] link;
		delete[] queue;
	}
	/* 初始化根节点 */
	void clear(){
		ad = 0;
		root = &link[ad ++];
		root->clear();
		root->fail = root;
	}
	/* 转换字符 */
	int cg(char ch){ return ch - 'a'; }
	/* 插入串 */
	void insert(char *str){
		automatonNode* rt = root;
		for(int i = 0; str[i] != '\0'; ++ i){
			if(rt->next[cg(str[i])] == NULL){
				rt->next[cg(str[i])] = &link[ad ++];
				rt->next[cg(str[i])]->clear();
			}
			rt = rt->next[cg(str[i])];
		}
		rt->real = 1;
	}
	/* 建立自动机 */
	void built(){
		automatonNode * fa;
		head = tail = tot = 0;
		queue[tail ++] = root;
		while(head != tail){
			fa = queue[head ++];
			if(fa->real == 0 && fa->id == -1) fa->id = tot ++;
			for(int i = 0; i < size; ++ i){
				if(fa->next[i] == NULL){
					fa->next[i] = (fa == root) ? (root) : (fa->fail->next[i]);
				}
				else{
					fa->next[i]->fail = (fa == root) ? (root) : (fa->fail->next[i]);
					fa->next[i]->real |= fa->next[i]->fail->real;
					queue[tail ++] = fa->next[i];
					if(fa->real) fa->next[i]->real = 1;
				}
			}
		}
	}
	/* 建立自动机的矩阵 */
	void buitMatrix(){
		automatonNode *rt;
		head = 0;
		matx.setZero();
		matx.init(tot, tot);
		while(head < tail){
			rt = queue[head ++];
			if(rt->real) continue;
			for(int i = 0; i < size; ++ i){
				if(rt->next[i]->real == 0)
					matx.mat[rt->id][rt->next[i]->id] ++;
			}
		}
	}
};

u64 numPowerMod(u64 x, u64 k){
	u64 ans = 1;
	while(k){
		if(k & 1) ans *= x;
		x *= x;
		k >>= 1;
	}
	return ans;
}
u64 numSumPowerMod(u64 x, u64 k){
	if(k == 1) return x;
	u64 sumTemp = numSumPowerMod(x, k >> 1), numTemp = numPowerMod(x, (k + 1) >> 1);
	if(k & 1) return sumTemp + sumTemp * numTemp + numTemp;
	else return sumTemp + sumTemp * numTemp;
}
matrix sumTemp, numTemp;
matrix matrixSumPowerMod(u64 k){
	if(k == 1) return matx;
	sumTemp = matrixSumPowerMod(k >> 1);
	numTemp = matx ^ ((k + 1) >> 1);
	if(k & 1) return sumTemp + sumTemp * numTemp + numTemp;
	else return sumTemp + sumTemp * numTemp;
}

void slove(u64 len){
	u64 ans = numSumPowerMod(26, len);
	matx = matrixSumPowerMod(len);
	for(int i = 0; i < matx.m; ++ i){
		ans -= matx.mat[0][i];
	}
	printf("%I64u\n", ans);
}

int main(){
	u64 ans;
	u64 n, l;
	char str[maxnl];
	automaton autom;
	while(~scanf("%I64u %I64u", &n, &l)){
		autom.clear();
		for(int i = 0; i < n; ++ i){
			scanf("%s", str);
			autom.insert(str);
		}
		autom.built();
		autom.buitMatrix();
		slove(l);
	} 
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值