题意:

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

思路:

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

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\n",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\n",dp[n]);
    }

}