zoukankan      html  css  js  c++  java
  • [HDU 3336]Count the String[kmp][DP]

    题意:

    求一个字符串的所有前缀串的匹配次数之和.

    思路:

    首先仔细思考: 前缀串匹配.

    n个位置, 以每一个位置为结尾, 就可以得到对应的一个前缀串.

    对于一个前缀串, 我们需要计算它的匹配次数.

    k = next [ j ]

    表示前缀串 Sj 的范围内(可以视为较小规模的子问题), 前缀串 Sk 是最长的&能够匹配两次的前缀串.

    这和我们需要的答案有什么关系呢?

    题目是求所有前缀串的匹配次数之和, 那么可以先求前缀串 Si 在整个串中的匹配次数, 再加和.


    到此, 用到了两个"分治", 一是将大规模的问题减小为小规模的问题, 二是将询问的最终结果拆分成一个个步骤, 则专注于分析核心步骤.


    可设dp[ i ]为前缀串 Si 在总串中出现的次数.

    dp[i] = 1;

    dp[next[i]] += dp[i];


    #include <cstring>
    #include <cstdio>
    const int MAXN = 200005;
    const int MOD = 10007;
    int dp[MAXN],next[MAXN];
    char s[MAXN];
    //46MS	1960K
    void prekmp()
    {
        next[0] = -1;
        int j = -1;
        for(int i=1;s[i];i++)
        {
            while(j!=-1 && s[j+1]!=s[i])    j = next[j];
            if(s[j+1]==s[i])    j++;
            next[i] = j;
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n;
            scanf("%d",&n);
            scanf("%s",s);
            prekmp();
            for(int i=0;i<n;i++)    dp[i] = 1;
            dp[n] = 0;
            for(int i=n-1;i;i--)
            {
                if(next[i]!=-1) {dp[next[i]] += dp[i];dp[next[i]] %= MOD;}
            }
            for(int i=0;i<n;i++)    {dp[n] += dp[i];dp[n] %= MOD;}
            printf("%d
    ",dp[n]);
        }
    }


    还有另一种思路:

    可以将前缀的匹配次数视为包含的前缀个数.

    最终的问题是求s中 (设每一种前缀i包含的前缀个数Fi ) ΣFi .

    dp[i]为前缀串s[0...i]包含的前缀个数的新增数目(相对于前缀串s[0...i-1]).

    dp[i] = dp[next[i]] + 1;//1表示它本身也是新增的一个前缀

    #include <cstring>
    #include <cstdio>
    const int MAXN = 200005;
    const int MOD = 10007;
    int dp[MAXN],next[MAXN];
    char s[MAXN];
    //62MS	1960K
    void prekmp()
    {
        next[0] = -1;
        int j = -1;
        for(int i=1;s[i];i++)
        {
            while(j!=-1 && s[j+1]!=s[i])    j = next[j];
            if(s[j+1]==s[i])    j++;
            next[i] = j;
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n;
            scanf("%d",&n);
            scanf("%s",s);
            prekmp();
            memset(dp,0,sizeof(int)*(n+1));
            for(int i=0;i<n;i++)
            {
                if(next[i]!=-1)
                {
                    dp[i] = dp[next[i]]+1;
                    dp[i] %= MOD;
                }
                else
                    dp[i] = 1;
            }
            for(int i=0;i<n;i++)    {dp[n] += dp[i];dp[n] %= MOD;}
            printf("%d
    ",dp[n]);
        }
    
    }
    


  • 相关阅读:
    LeetCode20 有效的括号
    函数的多个参数
    定义一个函数的基本语法 函数的参数
    函数
    金字塔
    水仙花数
    百鸡百钱
    循环demo
    while适用于不确定循环次数
    浏览器打断点
  • 原文地址:https://www.cnblogs.com/james1207/p/3320087.html
Copyright © 2011-2022 走看看