您的位置:首页 > 其它

Period UVALive - 3026(KMP)

2017-05-04 23:48 411 查看

Period(传送门

题意

给定字符串,找到每个前缀的最大循环节的个数,即循环周期,如果大于
1
的话就输出下标和周期数(循环节)

解题思路

KMP
就可以非常轻松的解决问题,首先提到一个
next
数组的性质,对于当前的前缀
a[j]
,如果
j % (j - next[j]) == 0
j - next[j]
即为最小周期长度,而周期数就是
j / (j - next[i])
,至于为什么
j - next[j]
为最小周期长度呢,请听我慢慢徐来,先看下面的示意图



通过看这幅图大家或许也能看懂,我就稍微讲述一下,我们当前失配了
j
点,然后我们需要调到
next[j]
,那我们知道
1子串
2子串
必须是相等的,就是公共后缀子串。

所以从
j
开始往
0
走的方向上,第一个长度为
k
和第二个长度为
k
的字符串是相等的。又因为
w == w
(因为
1==2
并且长度为
k
的后缀字符串也是相等的),所以
w字符串
的后缀长度为
k
的字符串等于以
j
next[j]
为结尾长度为
k
的后缀字符串。

以此类推,只要我们的
j % (j - next[j])
0
就可以保证从
j
往前走有
j / (j - next[j])
个长度为
j - next[j]
的字符串相等。

如此,证明完毕,如果不懂滴,可以留言。

代码

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <set>
#include <algorithm>

using namespace std;
typedef long long LL;
constexpr int MAXN = 2e6 + 5;
constexpr int MAXM = 4e4 + 5;
constexpr int mod = 20071027;
string b;
int n,nexts[MAXN];

void getNext(string a) {
int k = nexts[0] = -1;
int j = 0;
while(j < a.length()) {
if(k == -1 || a[j] == a[k]) {
j ++;
k ++;
nexts[j] = k;
} else {
k = nexts[k];
}
}
}

int main() {
ios::sync_with_stdio(false);
//ifstream in("input.txt");
//cin.rdbuf(in.rdbuf());
int cas = 1;
while(cin >> n && n) {
cin >> b;
getNext(b);
cout << "Test case #" << cas ++ << endl;
for(int i = 0; i <= n; i ++) {
if(i && i % (i - nexts[i]) == 0 && i / (i - nexts[i]) > 1) {
cout << i << " " << i / (i - nexts[i]) << endl;
}
}
cout << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: