HDU-3336 Count the string(KMP)

 

Count the string

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 12332    Accepted Submission(s): 5691

 

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

 

题意:求每一个前缀在字符串中出现的次数

思路:当然本身得出现一次,其次我们可以从每一个i位置开始不断取next,因为每取一次next,就代表有一个前缀出现一次,并且对于每一个i不断取next,产生的前缀串都是不会与之前重复的.还是需要深入理解next数组的含义.

-------------------------------------------分割线-------------------------------------------------------

2018.11.07 update.

太精妙了,简直是太精妙了!

时隔一年,今天我终于明白这道题用KMP到底该怎么做了!原来之前那种做法会稳T.(那时候还年轻不懂事......)

网上的很多做法是错误的,很简单的样例过不了  比如 abcabcab  answer:15     还很多(比如像我这个)会TLE,

这样暴力判断实际上最坏就是N^2的复杂度,1e5个a都承受不了.

 

其实大家发现没有,next数组是如此的神奇,他跳跳跳到底跳的什么,next[i]代表 s[0]~s[t-1] == s[i-t]~s[i-1],

next[i]代表是的长度,也是位置,他其实就表示前缀从0开始最长能多长(设这个位置是t)和以i-1结尾的串完全相同.

既然是两个完全相同的串,当我们执行j = next[i]的时候,这时的next[j]表示前缀从0开始最长能多长和以j-1结尾的串完全相同.

但是不要忘了,刚才我们得到了两个完全相同的串,现在是在其中一个串上求next,也就是在另一个串上求next,也就是说!!!  既然我们的头部0位置一直都没动,那么其实我们的尾部位置自始至终也没有动,既是在缩短尾部,也是在缩短头部.

唉,有点语无伦次,太精秒了!  看下面这张图吧,

黑色为主串,两段红色的是相同的,两段绿色的是相同的,两段蓝色是相同的,当我们在前一个红色上求next的时候,其实

也是在后一个红色段上求next ,连线的两段其实是相同的.

对于本题,我们不断的求next,其实可以理解为保持了尾部不动,所以每次求出的串都是新的,因为他们的尾部的位置在

之前都没有用过,但是这样不断跳next会TLE,现在再以前缀的思想来看,我们现在跳到的位置都是我们之前算过的,所以

我们完全可以做个DP,存一下现在算出来的数值,下次转移到此位置的时候直接用就可以了,不用重复计算了.

 

代码:

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 10007
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
const double esp = 1e-7;
const int ff = 0x3f3f3f3f;
 
int n,ne[maxn];
int f[maxn];
char s[maxn];
 
void get_next(char *s,int len)
{
	ne[0] = -1;
	int i = 0,j = -1;
	while(i< len)
	{
		if(j == -1||s[i] == s[j])
		{
			i++;j++;
			ne[i] = j;
		}
		else
			j = ne[j];
	}
}
 
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		mem(ne,0);
		mem(f,0);
		
		scanf("%d",&n);
		scanf(" %s",s);
		get_next(s,n);
		
		int ans = 0;
		for(int i = 1;i<= n;i++)//遍历每一个i
		{
			f[i] = f[ne[i]]+1;
			if(f[i] == mod) f[i] = 0;
			ans+= f[i];
			ans%= mod;
		}
		printf("%d\n",ans);
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值