AcWing 141. 周期(kmp求循环节)

一个字符串的前缀是从第一个字符开始的连续若干个字符,例如”abaab”共有5个前缀,分别是a, ab, aba, abaa, abaab。

我们希望知道一个N位字符串S的前缀是否具有循环节。

换言之,对于每一个从头开始的长度为 i (i>1)的前缀,是否由重复出现的子串A组成,即 AAA…A (A重复出现K次,K>1)。

如果存在,请找出最短的循环节对应的K值(也就是这个前缀串的所有可能重复节中,最大的K值)。
输入格式

输入包括多组测试数据,每组测试数据包括两行。

第一行输入字符串S的长度N。

第二行输入字符串S。

输入数据以只包括一个0的行作为结尾。
输出格式

对于每组测试数据,第一行输出 “Test case #” 和测试数据的编号。

接下来的每一行,输出具有循环节的前缀的长度i和其对应K,中间用一个空格隔开。

前缀长度需要升序排列。

在每组测试数据的最后输出一个空行。
数据范围

2≤N≤1000000

输入样例:

3
aaa
4
abcd
12
aabaabaabaab
0

输出样例:

Test case #1
2 2
3 3

Test case #2

Test case #3
2 2
6 2
9 3
12 4

题意:输入一个字符串,输出以i为结尾的字符串有几个最长循环节,1不用输出。

由于kmp的性质,i-next[i]就是最长循环节的长度,只要能整除就是存在。

using namespace std;
const int N=1000010;
char str[N];
int n,next[N];
void get_next(){
for(int i=2,j=0;i<=n;i){
while(j&&str[i]!=str[j+1])j=next[j];
if(str[i]==str[j+1])j;
next[i]=j;
}
}
int main(){
int cas=1;
while(~scanf(%d”,&n)){
if(n==0)break;
scanf(%s”,str+1);
get_next();
printf(“Test case #%d\n”,cas);
for(int i=2;i<=n;i){
int t=i-next[i];
if(i>t&&i%t==0)printf(%d %d\n”,i,i/t);
}
puts(“”);
}
return 0;
}

KMP算法的核心在于构建部分匹配表(也称为`next`数组),该表用于记录模式串的部分匹配信息。通过这些信息,可以在寻找重复子串的过程中优化查找效率。 要利用KMP算法来找出字符串的最小循环节,可以通过以下方法实现: ### 使用 KMP 找到最小循环节 对于一个长度为 `n` 的字符串 `s`,如果存在某个最小子串 `p` 能够通过多次复制构成原字符串,则这个子串即是最小循环节。以下是具体过程: #### 构建 Next 数组 定义一个辅助函数计算前缀函数值并存储于 next 数组中。此操作的时间复杂度为 O(n)[^1]。 ```python def compute_next_array(pattern): n = len(pattern) next_arr = [0] * n j = 0 for i in range(1, n): while j > 0 and pattern[i] != pattern[j]: j = next_arr[j - 1] if pattern[i] == pattern[j]: j += 1 next_arr[i] = j return next_arr ``` #### 计算最小循环节 基于上述得到的 next 数组,我们可以进一步推导出最小周期 t=n−next[n−1] 。当满足条件 (t 可整除 n),则表明 s 存在一个大小为 t 的基本单元反复拼接而成;否则整个字符串本身不具备更短的有效循环结构。 ```python def find_smallest_repeating_substring(s): next_arr = compute_next_array(s) length_of_string = len(s) period = length_of_string - next_arr[-1] if length_of_string % period == 0 and period != length_of_string: return s[:period] else: return s ``` 以上代码片段展示了如何应用 KMP 中的 next 数组特性去检测是否存在以及返回目标字符串内的最简重复序列。 ### 结论 综上所述,借助 KMP 算法不仅可以高效解决模式匹配问题,在特定场景下还能巧妙运用于诸如发现字符串内部隐藏规律的任务之中,比如本例中的定位最小循环单位即是如此。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值