BZOJ 1009:[HNOI2008]GT考试
题目链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1009
大意:给一个长度不大于20的数字串,求长度为N(10^9)的所有数字串中不包含该串的串的个数,结果对K取模。
这道题。。作为一个蒟蒻,感觉网上很多题解说的是KMP+矩乘,不是很容易理解。自己认为把问题转化成AC自动机(trie图)上的路径条数,可能好想一些(其实原理是一样的w)。
首先,对模板串建立一个AC自动机,然后对每个点将当遇到数字0-9各应该转移到哪个点处理出来(不向最后一个点连边,因为最后一个点表示匹配到了模板串),得到一张图。于是在该图上的1号点(ac自动机的起点)到所有点的长度为N的路径条数之和就是答案。
用矩阵快速幂搞一搞就好了,O(M^3logN)。实际操作中为了方便处理,加一个0号点作为终点,每个点向它连边,算的时候算长度为(N+1)的路径。
顺便推荐一个ac自动机的动画演示的网站:http://blog.ivank.net/aho-corasick-algorithm-in-as3.html
贴一下代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N, M, K;
struct Mat
{
int m[25][25];
Mat(int x = 0)
{
memset(m, 0, sizeof(m));
if(x) for(int i = 0; i < M; ++i) m[i][i] = x;
}
int *operator[](int k){return m[k];}
};
Mat operator*(Mat a, Mat b)
{
Mat c;
for(int i = 0; i < M; ++i){
for(int k = 0; k < M; ++k){
for(int j = 0; j < M; ++j){
c[i][j] = (c[i][j]+(ll)a[i][k]*b[k][j]%K)%K;
}
}
}
return c;
}
Mat operator^(Mat a, int b)
{
Mat r(1);
while(b){
if(b&1) r = r*a;
a = a*a; b >>= 1;
}
return r;
}
//前面都是矩阵部分
char s[25];
int fail[25], go[25][10];
int main()
{
// freopen("in.txt", "r", stdin);
scanf("%d%d%d", &N, &M, &K);
scanf("%s", s);
for(int i = 0; i < 10; ++i) go[0][i] = 1;
//建ac自动机(其实就是kmp求next数组)
for(int i = 0, j = 0; i < M; ++i){
while(j && s[j-1]!=s[i]) j = fail[j];
fail[i+2] = ++j;
}
// for(int i = 0; i < M+2; ++i) printf("fail[%d]=%d\n", i, fail[i]);
for(int i = 0; i < M; ++i){
for(int j = 0; j < 10; ++j){
if(j == s[i]-'0') go[i+1][j] = i+2;
else{
int t = fail[i+1];
while(t && s[t-1]-'0'!=j) t = fail[t];
go[i+1][j] = t+1;
}
}
}
Mat a; //建图
for(int i = 1; i < M+1; ++i){
for(int j = 0; j < 10; ++j){
int v = go[i][j];
if(v!=M+1) a[i][v]++;
}
a[i][0] = 1;
}
M++;
a = a^(N+1); //核心代码
printf("%d\n", a[1][0]);
return 0;
}
退役