洛谷P3435 [POI2006]OKR-Periods of Words题解(KMP)

题目链接:https://www.luogu.com.cn/problem/P3435

洛谷P3435 [POI2006]OKR-Periods of Words

KMP
题意为求给定字符串所有前缀的最长真循环节长度之和。
fail函数的应用,给定字符串的最长真循环节长度即为字符串长度减去最短非空公共前后缀长度,先求出原字符串的fail函数值。再从1开始遍历,若fail[i]能够继续减小(即它不是最短的公共前后缀),就令当前位置j=fail[j],直到fail[j]==-1时j所在位置即为原字符串的最短公共前后缀,将fail[i]直接赋值,并继续往后遍历,由于前面步骤已经求出了fail[i]的最小值,后继步骤的循环查找次数不会过多(此处相当于记忆化搜索),最后遍历一遍所有前缀并加上对答案的贡献即可,但要注意特判最短公共前后缀长度为0的情况。

#include<bits/stdc++.h>
#define next next_
#define y1 yy
#define hash hash_
#define complex complex_
using namespace std;

using ll=long long;
using ull=unsigned long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;

const ll mod=1e9+7;
const double pi=acos(-1.0);
int _;

ll n,fail[1000010];
string s;

void getfail(string s){
	ll len=s.size();
	fail[0]=-1;
	ll i,j;
	for(j=1;j<len;j++)
		for(i=fail[j-1];;i=fail[i]){
			if(s[j]==s[i+1]){
				fail[j]=i+1;
				break;
			}
			else if(i==-1){
				fail[j]=-1;
				break;
			}
		}
}

void work(){
	cin>>n>>s;
	getfail(s);
	for(ll i=1;i<n;i++){
		ll j=i;
		while(fail[j]!=-1) j=fail[j];
		if(fail[i]!=-1) fail[i]=j;
	}
	ll ans=0;
	for(ll i=0;i<n;i++){
		if(fail[i]==-1) continue;
		ans+=(i+1)-fail[i]-1;
	}
	printf("%lld",ans);
}

int main(){
//	scanf("%d",&_);
	_=1;
	while(_--){
		work();
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值