AC自动机+矩阵+二分

原创 2012年03月22日 17:21:01

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

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

题目大意:给出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;
}




AC自动机-多模式匹配算法

AC自动机        算法目的:        AC自动机主要用于解决多模式串的匹配问题,是字典树(trie树)的变种,一种伪树形结构(主体是树形的,但是由于加入了失败指针,使得它变成了一个有...
  • xaiojiang
  • xaiojiang
  • 2016年08月24日 12:05
  • 696

Codeforces 696D Legen...(AC自动机+矩阵快速幂)

http://codeforces.com/problemset/problem/696/D题意:给你n个串,每个串有个开心值,然后让你构造一个长为l的串,使得开心值之和最大。 题解:AC自动机处理...
  • Miracle_ma
  • Miracle_ma
  • 2016年07月29日 14:57
  • 655

ac自动机最详细的讲解,让你一次学会ac自动机。

在没学ac自动机之前,觉得ac自动机是个很神奇,很高深,很难的算法,学完之后发现,ac自动机确实很神奇,很高深,但是却并不难。 我说ac自动机很神奇,在于这个算法中失配指针的妙处(好比kmp算法中的...
  • creatorx
  • creatorx
  • 2017年05月02日 19:51
  • 16858

AC自动机 - 关于Fail指针

fail指针可以说是AC自动机里最难理解的东西,怎样更好的理解AC自动机的fail指针? 先来看一幅图: 看这幅图上的fail指针是怎么构造的. 树上的词分别是: { he , hers ...
  • u013371163
  • u013371163
  • 2017年03月05日 17:17
  • 675

Trie、KMP、AC自动机小结

最近做了不字符串的题,做下小结吧~          首先是Trie(也叫前缀树),Trie的结构并不难理解,Trie是个树形结构,它的每条边对应一个字符,每个节点对应一个字符串的前缀(根节点对应空串...
  • qian99
  • qian99
  • 2014年02月05日 21:58
  • 1468

Trie图 & AC自动机初学(1)

题目来源于:Hihocoder 时间限制:20000ms 单点时限:1000ms 内存限制:512MB 描述 前情回顾 上回说到,小Hi和小Ho接受到了河蟹...
  • qq_32036091
  • qq_32036091
  • 2016年05月08日 10:13
  • 1234

AC自动机总结

AC自动机总结 0.引言:       由于大连现场赛的一道 AC自动机+ DP的题目(zoj3545 Rescue the Rabbit)被小媛同学推荐看 AC自动机。经过一段时间的努力,终于把...
  • mobius_strip
  • mobius_strip
  • 2014年03月30日 02:18
  • 19910

AC自动机模板(数组+指针)hdu2222

Online Judge Online Exercise Online Teaching Online Contests Exercise Author F.A.Q Hand In...
  • u010660276
  • u010660276
  • 2014年08月20日 17:01
  • 530

AC自动机的一种简单实现

上述题目旨在希望编写一个可以查询目标串中多个模式串的出现次数的程序,属于典型的AC自动机的应用,利用Trie树和KMP匹配思想构建AC自动机可以快速有效的查找一个目标串中多个模式串的出现次数。...
  • VevoLiang
  • VevoLiang
  • 2016年06月14日 17:33
  • 618

java AC自动机

字典树结点 package ac_auto; import java.util.*; public class TrieNode { ...
  • judyge
  • judyge
  • 2014年11月26日 22:02
  • 939
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:AC自动机+矩阵+二分
举报原因:
原因补充:

(最多只允许输入30个字)