[BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】

题目链接:BZOJ - 1009

 

题目分析

题目要求求出不包含给定字符串的长度为 n 的字符串的数量。

既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j 位的字符串个数,然后转移就是可以从第 j 位加上一个字符转移到另一个位置。

然而..我并没有写过KMP + DP,我觉得还是写AC自动机+DP比较简单..于是,尽管只有一个模式串,我还是写了AC自动机+DP。

然后就是建出AC自动机,f[i][j] 表示长度为 i ,走到节点 j 的字符串的个数。然后 f[i][] 是由 f[i - 1][] 转移过来的。

这个 DP 的转移满足 f[i][j] = sigma(f[i - 1][k] * A[k][j]) ,所以可以用矩阵乘法优化。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>

using namespace std;

const int MaxL = 20 + 5;

int n, m, Mod, Ans, Root, Zero;
int Child[MaxL][11], Fail[MaxL];

char S[MaxL];

struct Matrix
{
	int Num[MaxL][MaxL];
} M;

Matrix operator * (Matrix A, Matrix B) 
{
	Matrix ret;
	memset(ret.Num, 0, sizeof(ret.Num));
	for (int i = 0; i < m; ++i)
		for (int j = 0; j < m; ++j)
		{
			if (A.Num[i][j] == 0) continue;
			for (int k = 0; k < m; ++k)
			{
				ret.Num[i][k] += A.Num[i][j] * B.Num[j][k];
				ret.Num[i][k] %= Mod;
			}
		}
	return ret;
}

Matrix Pow(Matrix a, int b)
{
	Matrix ret, f;
	memset(ret.Num, 0, sizeof(ret.Num));
	for (int i = 0; i < m; ++i) ret.Num[i][i] = 1;
	f = a;
	while (b)
	{
		if (b & 1) ret = ret * f;
		b >>= 1;
		f = f * f;
	}
	return ret;
}

void Prepare()
{
	Root = 0;
	for (int i = 0; i < m; ++i) 
		Child[i][S[i + 1] - '0'] = i + 1;
	Zero = m + 1;
	Fail[Root] = Zero;
	for (int i = 0; i <= 9; ++i) 
		Child[Zero][i] = Root;
	for (int i = 0; i < m; ++i)
		for (int j = 0; j <= 9; ++j)
			if (S[i + 1] - '0' == j) Fail[Child[i][j]] = Child[Fail[i]][j];
			else Child[i][j] = Child[Fail[i]][j];
	for (int i = 0; i < m; ++i)
		for (int j = 0; j <= 9; ++j)
			if (Child[i][j] != m) 
				++M.Num[i][Child[i][j]];
}

int main()
{
	scanf("%d%d%d", &n, &m, &Mod);
	scanf("%s", S + 1);
	Prepare();	
	M = Pow(M, n);
	Ans = 0;
	for (int i = 0; i < m; ++i) 
		Ans = (Ans + M.Num[0][i]) % Mod;
	printf("%d\n", Ans);
	return 0;
}

  

posted @ 2015-06-11 15:13  JoeFan  阅读(459)  评论(0编辑  收藏  举报