洛谷P3435 [POI2006]OKR-Periods of Words KMP算法

网址:https://www.luogu.org/problem/P3435

题意:

一个串是有限个小写字符的序列,特别的,一个空序列也可以是一个串。 一个串$P$是串$A$的前缀, 当且仅当存在串$B$,使得 $A = PB$. 并且$P$不是一个空串,那么我们说$P$是$A$的一个$proper$前缀。 定义$Q$是$A$的周期, 当且仅当$Q$是$A$的一个$proper$前缀并且$A$是$QQ$的前缀(不一定要是$proper$前缀)。 比如串 $abab$ 和 $ababab$ 都是串 $abababa$ 的周期。串A的最大周期就是它最长的一个周期或者是一个空串(当$A$没有周期的时候)。比如说, $ababab$ 的最大周期是 $abab$。 串 $abc$ 的最大周期是空串。给出一个串,求出它所有前缀的最大周期长度之和。

说实话我也没有看懂,然后就试着自己理解了一下,$proper$前缀就是一个不为空串的前缀。$Q$是$A$的一个$proper$前缀且$A$是$QQ$的前缀,也就是$Q$的两倍能够覆盖$A$。对于给出的字符串,将其每个前缀设为$A$,求最大长度的$Q$,输出它们的和。

题解:

使用$KMP$算法:实际上是求每个前缀的最长前缀,所以处理完一次$next$数组后,对于$next$数组的除$next[0]$的每个值。设为$next[i]$,从前往后遍历$next$数组,如果$next[next[i]]$为$0$,则说明该前缀的最大前缀长度为$0$,不用处理。否则改为$next[next[i]]$,然后累加$i-next[i]$,此处如何理解呢?假设求得$next[next[i]]=a$,且$next[i]$没有被修改过,$a$就是以长度为$i$的字符串的最长前缀后缀相同长度的子串的最长前后缀相同长度。修改为$next[i]$,当后面有一个$j$,$next[j]=i$时,即$next[next[j]]=a$时,即原来的长度为$i$的子串是长度为$j$的子串的最长前缀,相当于增加了一个周期,此时自然是修改为$a$,即先找到最简单的串,即如果找到了$aba$,$next[3]=1$,后面扩展到$ababa$时增加了一个$ab$,即最小周期,此时不改变,以维持最长长度。$next[0]=-1$不需要处理。

AC代码:

#include <iostream>
#include <string>
using namespace std;
int nextarray[1000005];
void getnext(string &str)
{
	nextarray[0] = -1;
	int j = -1, k = 0;
	while (k < str.size())
	{
		if (j == -1 || str[j] == str[k])
			nextarray[++k] = ++j;
		else
			j = nextarray[j];
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n;
	cin >> n;
	string str;
	cin >> str;
	getnext(str);
	long long ans = 0;
	for (int i = 1; i <= n; ++i)
		if (nextarray[nextarray[i]] > 0)//有前缀的前缀
			nextarray[i] = nextarray[nextarray[i]];
	for (int i = 1; i <= n; ++i)
		if (nextarray[i])
			ans += i - nextarray[i];
	cout << ans << endl;
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/Aya-Uchida/p/11295770.html