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;
}

posted @ 2017-02-09 17:04  will7101  阅读(116)  评论(0编辑  收藏  举报