1026 - KMP - OKR-Periods of Words[POI2006]

传送门

Little Tip

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

分析

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

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

小栗子献上:
a b c a b abcab abcab n x t [ 5 ] = 2 , n x t [ 4 ] = 1 , n x t [ 3 ] = 0 , n x t [ 2 ] = 0 , n x t [ 1 ] = 0 nxt[5]=2,nxt[4]=1,nxt[3]=0,nxt[2]=0,nxt[1]=0 nxt[5]=2nxt[4]=1,nxt[3]=0,nxt[2]=0,nxt[1]=0

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


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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值