hdu-3336 Count the string

Count the string
题目链接
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13126 Accepted Submission(s): 6029

Problem Description
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

Author
foreverlin@HNU

Source
HDOJ Monthly Contest – 2010.03.06

Recommend
lcy | We have carefully selected several similar problems for you: 1711 1686 3746 1358 3341

题意
给你 T 组数据,每组有一个 n ≤ 2*10^5 和一个长度为 n 的字符串。输出这个字符串的任意前缀在整个串中出现的次数加和 mod 10007。

题解
我想大家都可以猜到这就是KMP的类型题,但是怎么做呢?

这里写图片描述

首先考虑暴力的想法。

这里写图片描述

如果可以匹配,那么ans+1。
根据KMP中p(或nxt或lst等,就是S串中以i为结尾的一个子串尽可能长,且等于S串的某个前缀)数组的定义,我们可以假设当前的 i 和 j 满足:i = p[j]

这个东西可以通过KMP来求,复杂度就成了O(2n),但是答案怎么累计?

这里写图片描述

我们接下来我们就要看看 k2~j 的子段里是否存在是前缀的字段。实际上就是看看 1~i2 (即1~p[j])这段里存在的所有是前缀的子段。
这也就是某个 j‘ 等于 i2 时我们求出的子段个数,需要再看看 1~p[j’] 的 继续往下递归……
记忆化一下(也就是DP),f[i] 表示 i-p[i]+1 ~ i (或者是1 ~ p[i])这段中为前缀的子段个数,求出当前 p[i] 后, f[i] = f[p[i]] + 1。
初始化:p[0]=0(字符串下标从1开始)

代码
代码里的字符串是从 0 开始的。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=200005;
char s[maxn];
int L,p[maxn];
long long ans,f[maxn];
void F()
{
    scanf("%d",&L);
    memset(s,0,sizeof s);
    scanf("%s",s);
    int j=p[0]=-1;
    f[0]=ans=1;//小心第1个别漏了
    for (int i=1;i<L;i++)
    {
        while (j>-1&&s[i]!=s[j+1]) j=p[j];
        p[i]=j+=s[i]==s[j+1];
        f[i]=p[i]<0?1:f[p[i]]+1;
        ans=(ans+f[i])%10007; 
    }
    printf("%lld\n",ans);
}
int main()
{
    int T;scanf("%d",&T);
    while (T--) F();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/80630743
今日推荐