Count the string
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4845 Accepted Submission(s): 2295
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.
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.
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
这道题由于n可以到200000,用任何O(n^2)的办法都是不合理的,可以用O(n)办法处理
首先,必须先透彻理解kmp的用法,尤其是其中next数组的含义
简单来说,它表示的是这个位置的前一位置到最开头的那一串的前缀后缀最大值
不会kmp?请看这位大神的博客
http://blog.youkuaiyun.com/v_july_v/article/details/7041827 写的非常详细透彻,我也是看过才会的
假设你会了
先注意一下,这个题目要求的子串全部是从头开始的,这就非常符合kmp算法
那在这个基础上,就可以进行一次dp,这个方程是通过next数组的基本含义所发现然后推出来的
我们假设当前走到的位置是i,那么它表示的是从0到 i-1这个串的前缀后缀最大值
令j=next[i],j表示前缀后缀最大值,j-1就表示最长前缀后缀的尾指针,i-1表示的是我现在位置的前一个位置,也就是现在这个串(0..i-1)的尾指针
设ans[i] 表示0 到 i 这个串前缀后缀的总数
你会不难发现,ans[i-1]和ans[j-1]之间有些关系(请注意i和i-1,j和j-1分别表示什么)
ans[i-1]=ans[j-1]+1;
也就是说,从0到j-1这个串的所有前缀后缀都在0到i-1这个串中出现一次。而 +1,代表算上0到j-1这个串
最后加上一下所有的ans[i] (0=<i<n) ,完了么?没有,因为每个串本身还要算一次,所有最后答案加上n即可,不要忘记中间取模和最后取一次模
如果觉得我讲的很迷,就你就按照next数组的思想去想dp方程吧。
前提是一定要把kmp理解透彻
//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi 3.1415926535898
#define eps 1e-6
#define MOD 10007
#define MAXN
#define N 200100
#define M
int nextpos[N],n,ans[N],lastans;
//nextpos是kmp算法中的next数组
//ans表示从0到i这个串中前缀后缀总数,就是答案的一部分
char s[N];
void build_nextpos()
{//kmp算法中最重要的next数组
int i,j;
i=0;
j=-1;
nextpos[0]=-1;
while(i<n)
{
if(j==-1 || s[i]==s[j])
{
nextpos[i+1]=j+1;
i++;
j=j+1;
}
else j=nextpos[j];
}
}
int main()
{
int T,j;
cin>>T;
while(T--)
{
scanf("%d %s",&n,s);
repin(i,0,n)
{
ans[i]=0;
}
build_nextpos();
repin(i,0,n)
{
j=nextpos[i];
if(j>0 && i>0)
{//dp方程
ans[i-1]=ans[j-1]+1;
ans[i-1]%=MOD;
}
}
lastans=0;
rep(i,0,n)
{//最后统计答案
lastans+=ans[i];
lastans%=MOD;
}
//由于每个串本身还要算一次,所以+n
lastans+=n;
lastans%=MOD;
printf("%d\n",lastans);
}
}