• 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
    ", 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
    ", a[1][0]);
        return 0;
    }
    
    
  • 相关阅读:
    Spring Boot 搭建项目阶段Group和Artifact的含义
    设置Mysql数据库账号密码以及时区
    反射
    线程与进程
    网络编程
    队列和栈
    linux下的mysql
    积累的关于linux的安装卸载软件基本命令
    各种url编码
    解决浏览器传值乱码
  • 原文地址:https://www.cnblogs.com/will7101/p/6506684.html
Copyright © 2020-2023  润新知