[CSP-S模拟测试]:串串香(KMP)

题目传送门(内部题75)


输入格式

  输入文件$ccx.in$
  每个输入文件包含多组测试数据。输入文件的第一行为一个整数$T$,表示数据组数。接下来$T$行,每行表示一组测试数据
  每行一开始,两个空格隔开的数字$n,m$,含义见【题目描述】。之后是一个长度为$m$的字符串。


输出格式

  $T$行,每行一个整数表示答案。


样例

见下发文件


数据范围与提示

  对全部测试数据,$nleqslant 10^6 ,mleqslant 10^6 ,Tleqslant 10$,输入文件中的所有$m$之和不超过$5 imes 10^6$
  第$1,2$个测试点,$m=1$
  第$3,4$个测试点,给出的字符串中只含有字母$'A'$
  第$5,6,7,8,9,10,11$个测试点,满足$n imes mleqslant 10^6$
  第$11,12,13,14,15$个测试点,满足$mleqslant 100$
  第$16,17,18,19,20$个测试点,无特殊限制


题解

考察对$KMP$的理解。

一般情况下就是$(n-1) imes m$。

话画图会发现当串长减去最长公共前后缀是串长的一个因子时答案就是$(n-1) imes m+nxt[n]$(这里的$nxt$数组其实就是$KMP$中的$nxt$数组,即为串的公共前后缀)。

其实用$hash$也可以。

时间复杂度:$Theta(T imessum m)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

$KMP$:

#include<bits/stdc++.h>
using namespace std;
long long n,m;
char ch[2000001];
int nxt[2000001];
void KMP()
{
	nxt[0]=-1;
	for(int i=0;i<m;i++)
	{
		int j=nxt[i];
		while(j!=-1&&ch[i]!=ch[j])j=nxt[j];
		nxt[i+1]=++j;
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld",&n,&m);
		scanf("%s",ch);
		KMP();
		if(n==1)printf("%d
",nxt[m]);
		else if(!(m%(m-nxt[m])))printf("%lld
",(n-1)*m+nxt[m]);
		else printf("%lld
",(n-1)*m);
	}
	return 0;
}

$hash$:

#include<bits/stdc++.h>
using namespace std;
long long n,m;
char ch[1000001];
unsigned long long mod[1000001],Hash[1000001];
int HASH()
{
	mod[1]=1;
	Hash[1]=ch[1]-'A'+1;
	for(int i=2;i<=m;i++)
	{
		Hash[i]=Hash[i-1]*131+ch[i]-'A'+1;
		mod[i]=mod[i-1]*131;
	}
	for(int i=m-2;i;i--)
		if(Hash[i+1]==Hash[m]-Hash[m-i-1]*mod[i+2])return i+1;
	return 1;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld",&n,&m);
		scanf("%s",ch+1);
		int flag=HASH();
		if(n==1)printf("%d
",flag);
		else if(!(m%(m-flag)))printf("%lld
",(n-1)*m+flag);
		else printf("%lld
",(n-1)*m);
	}
	return 0;
}

rp++

原文地址:https://www.cnblogs.com/wzc521/p/11717563.html