hdu3336kmp,dp

找出前缀后,算出现次数,很明显的是一个单模式串匹配问题,KMP 可以很好的解决,不过如果直接这样暴力的话,O(n^2) 的复杂度还是不行的。。。因此,我们试着考虑 KMP 算法进行快速匹配的本质核心所在,其实就是 next[] 数组

而这个的本质其实就是 S[1..next[i]]=S[i-next[i]+1…i]

即模式串的最长公共前后缀串的长度

举个例子 ababa

我们要算这个字符串的前缀的出现次数和

a 出现 3

ab 出现 2

aba 出现 2

abab 出现 1

ababa 出现 1

那么我们可以这样来 DP

记 dp[i] 为前 i 个字符组成的前缀出现的次数

则 dp[next[i]]+=dp[i]

这个转移方程是什么含义呢???

我们可以这样来想

如 dp[3] 对应 aba 且 next[5]=3

则 dp[3]+=dp[5] 为答案

因为 S[1..next[i]]=S[i-next[i]+1…i] aba 自己出现了 dp[3] ,然后 S[i-next[i]+1..i] 出现了 dp[5] 也是 aba 会出现的地方,因此也要加上
初始化的时候,记得 dp[i]=1 表示自身匹配算 1 次

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
char s[200005];
int Next[200005];
int dp[200005];
int n;

int getNext()
{
    int k=0;
    int sum=1;
    int len=strlen(s);
    Next[0]=0;
    dp[0]=1;
    for(int i=1;i<len;i++)
    {
        while(k>0&&s[k]!=s[i])
        k=Next[k-1];
        if(s[k]==s[i])
        k++;
        if(k>0)
        dp[i]=(dp[k-1]+1)%10007;
        else
        dp[i]=1;
        sum=(sum+dp[i])%10007;
        Next[i]=k;
    }
    return sum;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%s",&n,s);
        printf("%d\n",getNext());
    }
    return 0;
}

你可能感兴趣的:(hdu3336kmp,dp)