zoukankan      html  css  js  c++  java
  • [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
    ", Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    操作系统上机实验
    选择排序
    插入排序(c++)
    嵌入式原理实验代码集合
    iOS应用程序生命周期(前后台切换,应用的各种状态)详解
    ios Base64编解码工具类及使用
    iOS:横向使用iPhone默认的翻页效果
    ios学习笔记之内存管理
    ios NavBar+TarBar技巧
    IOS设备滑动事件
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4569178.html
Copyright © 2011-2022 走看看