HDU 3336 Count the string(深入理解KMP算法)【模板】

It is well known that AekdyCoin is good at string problems as well as number theory problems. When given a string s, we can write down all the non-empty prefixes of this string. For example: 
s: "abab" 
The prefixes are: "a", "ab", "aba", "abab" 
For each prefix, we can count the times it matches in s. So we can see that prefix "a" matches twice, "ab" matches twice too, "aba" matches once, and "abab" matches once. Now you are asked to calculate the sum of the match times for all the prefixes. For "abab", it is 2 + 2 + 1 + 1 = 6. 
The answer may be very large, so output the answer mod 10007. 
Input
The first line is a single integer T, indicating the number of test cases. 
For each case, the first line is an integer n (1 <= n <= 200000), which is the length of string s. A line follows giving the string s. The characters in the strings are all lower-case letters. 
Output
For each case, output only one number: the sum of the match times for all the prefixes of s mod 10007.
Sample Input
1
4
abab
Sample Output
6

【题解】

      前几天还写了两道kmp的题,不过当时只是照挪模板,没有深入理解这个算法的真谛,一直不懂,今天这个题想自己先写,试试能不能写出来,结果不知道从何下笔,无奈之下百度了一下,看到一篇博客开头几句,我就恍然大悟,重点在next数组上,我又专门看了next数组的含义,又自己尝试了几次,结果就ac了,喜不自禁。

      next数组,KMP算法的核心所在,弄懂了next数组的含义,这算法也就很简单了,下面来一起理解一下。

     我就举例子说吧,next数组的值就是记录当前位置向前多少个和这个字符串开头多少个相等。比如样例中的abab,因为字符串首字母的下标是0,我现在把它定义为下标从1开始,就要在字符末尾再随便加一位(具体看代码),那么这个字符串的next数组值依次是0,0,1,2;

     前两个0代表第i个字符向前0个字符和这个字符串开头0个相等,我们可以看到,第一个字符a和第二个字符在之前都没出现过,所以就是0,而那个1则代表从第三个字符向前一位开始到第三个字符这一段(这里其实就是a这一个)和字符串前1个相等,其实就是两个a相等,而最后一个2就代表最后的ab这段和开头前两个(开头ab这一段)相等,这下应该懂了吧,把这个样例好好看看,动手模拟一下就彻底明白了。

     这样以后,我们就可以再用个数组把每一段出现的次数保存一下,比如代码中的

for(int i=1;i<=m;++i)
        {
            sum[next_t[i]]++;
        }
       以前面的样例为例(0,0,1,2),就是后面的长度为0的有2个,长度为1的有一个,长度为2的有一个,而长度为0则代表后面没出现过,所以不算进去,所以后面重复出现的就有1个a和1个ab,再加上初始的(a,ab,aba,abab),就一共有6个了。

     仔细理解下。

【AC代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=10007;
const int N=200005;
int m;
char str[200005];
int sum[N];
int next_t[N];

void get_next()//next数组模板
{
    int i=0,j=-1;
    next_t[0]=-1;
    while(i<m)
    {
        while(j!=-1 && str[i]!=str[j])
            j=next_t[j];
        i++;
        j++;
        next_t[i]=j;
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%s",&m,str);
        memset(sum,0,sizeof(sum));
        str[m]='z';//为了让数组从1开始,末尾加一位,不影响结果,不信的话自己改变赋值测试
        int ans=0;
        get_next();
        for(int i=1;i<=m;++i)
        {
            sum[next_t[i]]++; //统计重复出现次数
        }
        for(int i=1;i<=m;++i)
        {
            ans+=sum[i];//统计重复出现的总个数
            ans%=inf;
        }
        ans+=m;//记得加上初始的总个数
        printf("%d\n",ans%inf);
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值