天天看点

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

 做此题前,建议先做POJ2778  附有详细图解

 这题差不多在POJ2778基础上,只不过要求长度不超过L的所有串。

数据范围很大,总共有 26 + 26^1 + 26^2 + ....... + 26^n种可能,减去不包含词根的情况就行。

需要求每次乘矩阵第一行的和那就简单了,只要增加一列全为1, a[1][n+1]表示矩阵第一行和n+1列相乘之和

也就是每次都求和,最后减多余的 1 ( 1*1=1 )。要求mod 2^64,只需要写成unsigned long long,自动处理溢出 

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxn =  200;
int trie[maxn][26]; //字典树
int cntword[maxn];  //记录该单词出现次数
int fail[maxn];     //失败时的回溯指针
int cnt = 0;//结点个数
bool tag[maxn];
ll L;
int n;
struct Mat {
	ull m[101][101];
};
Mat mul(Mat x,Mat y,int si) {
	Mat c;
	for(int i=0; i<si; i++) {
		for(int j=0; j<si; j++) {
			c.m[i][j] = 0;
		}
	}
	for(int i=0; i<si; i++) {
		for(int j=0; j<si; j++) {
			for(int k=0; k<si; k++) {
				c.m[i][j] = (c.m[i][j]+ x.m[i][k]*y.m[k][j]);
			}
		}
	}
	return c;
}
Mat poww(Mat x,ll y,int si) { //求矩阵x的y次幂
	Mat as;
	for(int i=0; i<si; i++)
		for(int j=0; j<si; j++)
			if(i==j)as.m[i][i] = 1;
			else as.m[i][j] = 0;//防止多次输入,清零

	while(y) {
		if(y&1)as = mul(as,x,si);
		x = mul(x,x,si);
		y>>=1;
	}
	return as;//返回ans
}
void insertWords(char s[],int len) {
	int root = 0;
	for(int i=0; i<len; i++) {
		int next = s[i] - 'a';
		if(!trie[root][next])
			trie[root][next] = cnt++;
		root = trie[root][next];
	}
	tag[root]=true;      //当前结点单词数+1
}
void getFail() {
	queue <int>q;
	for(int i=0; i<26; i++) {   //将第二层所有出现了的字母扔进队列
		if(trie[0][i]) {
			fail[trie[0][i]] = 0;
			q.push(trie[0][i]);
		}
	}
	while(!q.empty()) {
		int now = q.front();
		q.pop();
		if(tag[fail[now]])tag[now] = true;
		for(int i=0; i<26; i++) {
			if(trie[now][i]) {
				fail[trie[now][i]] = trie[fail[now]][i];
				q.push(trie[now][i]);
			} else trie[now][i] = trie[fail[now]][i];
		}
	}
}

void query() {
	ull ans = 0;
	Mat c,d;
	for(int i=0; i<cnt+1; i++) {
		for(int j=0; j<cnt+1; j++) {
			c.m[i][j] = 0;
			d.m[i][j] = 0;
		}
	}
	for(int i=0; i<cnt; i++) {
			if(tag[i])continue;
		for(int j=0; j<26; j++) {
			if(!tag[trie[i][j]])c.m[i][trie[i][j]]++;
		}
	}
	for(int i=0; i<=cnt; i++)c.m[i][cnt] = 1;
	cnt++;
	c = poww(c,(ll)(L),cnt);
	d.m[0][0] = 1;
	d.m[0][1] = 26;
	d.m[1][0] = 0;
	d.m[1][1] = 26;
	d = poww(d,(ll)L,2);
	for(int i=0; i<cnt; i++)ans+=c.m[0][i];
	ull res1 = d.m[0][1];
	ans = res1 - ans +1;
	cout<<ans<<endl;
}
int main() {
	char s[1000005];
	while(scanf("%d %lld",&n,&L)==2) {
		memset(fail,0,sizeof(fail));
		memset(trie,0,sizeof(trie));
		memset(tag,false,sizeof(tag));
		cnt = 1;
		for(int i=0; i<n; i++) {
			scanf("%s",s) ;//输入单词
			insertWords(s,strlen(s));
		}
		fail[0] = 0;
		getFail();
		query();
	}

	return 0;
}
/*
4
ash shex bcd sha
ashex

*/