传送门

Little Tip

这道题,千万不要看中文翻译!!!
我一开始就看的中文,看得我一脸懵逼,什么什么什么鬼啊
后来看了题解,还是一脸懵逼
再后来滚去看英文版本的,喔~~~
豁然开朗
不过既然都说到这个份上了
大家是不是都觉得我要翻译一下了啊
当然不是。
自己去翻。
英文还是蛮好懂的

分析

KMP是真的妙,一定要深入理解 nxt[i]nxt[i] 的含义

nxt[i]nxt[i] : 以ii作为结尾的,最长的,相同前后缀的长度

小栗子献上:
abcababcabnxt[5]=2nxt[4]=1,nxt[3]=0,nxt[2]=0,nxt[1]=0nxt[5]=2,nxt[4]=1,nxt[3]=0,nxt[2]=0,nxt[1]=0

对于这道题,我们需要求串A的每一个前缀的最大周期之和
也就是求任意一个串的最大周期,然后相加
最大周期就是,,,我们先定义串B:满足B是A的一个前缀,且A是两倍B的前缀
最大周期就是串B的最大的长度
来看一张图
(图摘自洛谷)
1026 - KMP - OKR-Periods of Words[POI2006]
我们发现inxt[i]i-nxt[i]就是一个周期,当nxt[i]nxt[i]最小的时候,求得的就是最大周期
这同时也满足了A是两倍串B的条件,为什么呢?
nxt[i]>leni/2nxt[i]>leni/2,此时inxt[i]<leni/2i-nxt[i]<leni/2肯定是错误的
但由于我们始终是在找nxt[i]nxt[i]的最小值,当nxt[i]>leni/2nxt[i]>leni/2时,肯定还可以找到更小的
1026 - KMP - OKR-Periods of Words[POI2006]
最后我们稍微优化一下


Code

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
char st[1000009];
int nxt[1000009];
int main(){
	int len,i,j;
	scanf("%d",&len);
	scanf("%s",st+1);
	nxt[1]=0;j=0;
	for(i=2;i<=len;++i){
		while(j&&st[j+1]!=st[i]) j=nxt[j];
		if(st[j+1]==st[i]) j++;
		nxt[i]=j;
	}
	ll ans=0;
	for(i=1;i<=len;++i){
		j=i;
		while(nxt[j]) j=nxt[j];
		if(nxt[i]) nxt[i]=j;//优化
		ans+=1ll*(i-j);
	}
	cout<<ans;
	return 0;
}

相关文章:

  • 2022-12-23
  • 2022-01-31
  • 2021-11-05
  • 2021-09-29
  • 2022-12-23
猜你喜欢
  • 2021-06-12
  • 2022-02-20
  • 2021-05-29
  • 2021-08-23
  • 2022-01-30
相关资源
相似解决方案