zoukankan      html  css  js  c++  java
  • 【bzoj5130】[Lydsy12月赛]字符串的周期 DFS+KMP

    题目描述

    给定 $n$ 和 $m$ ,求所有 长度为 $n$ ,字符集大小为 $m$ 的字符串,每个前缀的最短循环节长度乘积 的总和。

    $nle 12,mle 10^9$


    题解

    DFS+KMP

    对于字符串中的每一种字符,将其看作:该字符第一次出现位置之前的字符种类数+1,把得到的序列称为“该字符串的最小表示”。

    那么显然本题中最小表示相同的字符串的答案是一样的。$n$ 很小,因此可以暴搜最小表示序列,然后计算贡献,乘以最小表示为这个序列的字符串个数统计到答案中。

    统计贡献时使用到KMP的一个小结论:长度为 $n$ 的字符串最短循环节长度为 $n-next[n]$ 。因此求出 $next[]$ ,对每个前缀算一下乘起来。

    最小表示为该序列的字符串个数是一个排列数,为 $A_{m}^{字符种类数}$ ,预处理排列计算即可。

    经过一个小dp程序可以计算出 $n=12$ 时最小表示个数为4213597,因此时间复杂度为 $O(4213597·12)$ 

    #include <cstdio>
    #define mod 998244353
    typedef long long ll;
    int s[13] , next[13] , n , m;
    ll a[13] , ans;
    void dfs(int p , int v)
    {
        int i;
        if(p == n)
        {
            int j;
            ll ret = 1;
            for(j = -1 , i = 1 ; i <= n ; i ++ )
            {
                while(~j && s[j] != s[i - 1]) j = next[j];
                next[i] = ++j , ret = (ret * (i - j)) % mod;
            }
            ans = (ans + ret * a[v]) % mod;
            return;
        }
        for(i = 1 ; i <= v ; i ++ ) s[p] = i , dfs(p + 1 , v);
        if(v + 1 <= m) s[p] = v + 1 , dfs(p + 1 , v + 1);
    }
    int main()
    {
        int i;
        scanf("%d%d" , &n , &m);
        a[0] = 1;
        for(i = 1 ; i <= n && i <= m ; i ++ ) a[i] = a[i - 1] * (m - i + 1) % mod;
        next[0] = -1 , dfs(0 , 0);
        printf("%lld
    " , ans);
        return 0;
    }
    
  • 相关阅读:
    Integer类的parseInt和valueOf的区别
    华为实习小结
    程序员浪费生命的几种方式
    移动前端中viewport(视口) 转
    Console API 与命令行
    Ajax
    浏览器缓存机制
    mysql之各种命令总结
    jquery file upload 文件上传插件
    文件上传插件uploadify详解
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8182433.html
Copyright © 2011-2022 走看看