【算法竞赛进阶指南】141.周期 题解 KMP 最小循环节

题目描述

一个字符串的前缀是从第一个字符开始的连续若干个字符,例如 abaab 共有 5 5 5 个前缀,分别是 aababaabaaabaab

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

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

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

输入格式

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

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

第二行输入字符串 S S S

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

输出格式

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

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

前缀长度需要升序排列。

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

数据范围

2 ≤ N ≤ 1000000 2 \le N \le 1000000 2N1000000

输入样例:
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

题意重述

编写一个程序,对于每一个前缀子串 s i s_i si,找出它的最短循环节的重复次数。 s i s_i si 的最短循环节是指连续组合能恰好构成 s i s_i si 的最短的子串。例如,对于字符串 “aabaabaa”,“aab” 不是其最短循环节,因为它无法恰好构成原串。

注意,在以下叙述中,下标从1开始。

算法

通过KMP算法可以求得next数组,对于每一个前缀子串 s i s_i si,其最短循环节的长度就是 i − next [ s i ] i - \text{next}[s_i] inext[si]
为什么呢?

首先看图,上下两条线表示同一个字符串,重合的部分表示KMP匹配(前后缀相等的最大长度)。
上面的1就是下面的1(完全相同的部分),而下面的1等于上面的2(前后缀匹配),上面的2等于下面的2,而下面的2等于上面的3…
所以上面的1,2,3,4,5和下面的1,2,3,4,5完全相同。

image-20230530151038382

在不严格要求“恰好”构成时,每一小段都可以视作原串的循环节。然后,我们可以证明,这样的小段就是原串的最短循环节
那么,如何证明呢?

反证: 假设该小段字符串T不是最短循环节,则原串中必然存在T’作为最短循环节。那么对于T’,可以按照上图的方式,将上下两串划分为一个个由T’组成的部分。

image-20230530152735547

此时矛盾出现:如果可以用更小的T’划分,则根据图示,原串的KMP匹配长度就是 n − len ( T ′ ) n - \text{len}(T') nlen(T)。这个长度 n − len ( T ′ ) n - \text{len}(T') nlen(T) 超过了 n − len ( T ) n - \text{len}(T) nlen(T),而 n − len ( T ) n - \text{len}(T) nlen(T) 是原串的最大前后缀匹配长度。所以假设是错误的,也就是说,T确实是最短循环节。

当不严格要求“恰好”构成时, n − len ( T ) = n − next [ n ] n - \text{len}(T) = n - \text{next}[n] nlen(T)=nnext[n] 就是最短循环节的长度。对于每一个前缀子串 s i s_i si,其最短循环节的长度就是 i − next [ i ] i - \text{next}[i] inext[i]。那么,当我们严格要求恰好构成时,又会是怎样呢?

我们可以证明一个引理,一个字符串的任何循环节(除最短循环节外)都是最短循环节的倍数。也就是说,不存在其他可能的模式使得原串能够恰好由其循环构成。因此,如果原串长度能被 l e n ( T ) len(T) len(T) 整除,则存在最短循环节且为 s [ 1 ∼ l e n ( T ) ] s[1\sim len(T)] s[1len(T)],如果不能被整除,则不存在(按题目的要求,不能“恰好”构成就是为不存在)。

引理证明:

反证: 假设原串存在一个子串T’,它不是最短循环节,也不是最短循环节的循环构成(倍数),但是可以循环构成原串。(有点绕)

这就意味着 l e n ( T ′ ) > l e n ( T ) len(T') > len(T) len(T)>len(T),且 l e n ( T ′ ) len(T') len(T) 不是 l e n ( T ) len(T) len(T) 的倍数。根据循环节的定义,T 和 T’ 都可以构成原串。即原串可以写为 T T . . . T A TT...TA TT...TA(T出现 m 次)或 T ′ T ′ . . . T ′ B T'T'...T'B TT...TB(T’出现 n 次)。这里的 A 和 B 可能是空串,或者长度不足一个 T 或 T’ 的部分。满足 m × l e n ( T ) = n × l e n ( T ′ ) m \times len(T) = n \times len(T') m×len(T)=n×len(T)

于是我们可以找到一个更小的循环节,其长度为d,因为
s j = s j + l e n ( T ) = s j + 2 l e n ( T ) = ⋯ = s j + x l e n ( T ) = s j + x l e n ( T ) − l e n ( T ′ ) = s j + x l e n ( T ) − 2 l e n ( T ′ ) = ⋯ = s j + x l e n ( T ) − y l e n ( T ′ ) = s j + d s_j=s_{j+len(T)}=s_{j+2len(T)}= \cdots =s_{j+xlen(T)}=s_{j+xlen(T)-len(T')}=s_{j+xlen(T)-2len(T')}=\cdots =s_{j+xlen(T)-ylen(T')}=s_{j+d} sj=sj+len(T)=sj+2len(T)==sj+xlen(T)=sj+xlen(T)len(T)=sj+xlen(T)2len(T)==sj+xlen(T)ylen(T)=sj+d

此处的 j j j 可以从几乎任意位置开始,只要字符串的长度足够长以支持所描述的周期 (如果不支持,那么表示从j开始的后续部分无法使用T’进行重复构成,这样的情况则不需要讨论。)。

但这与T是最短循环节的假设产生矛盾,假设不成立。故不存在一种循环节使得它既不是最短循环节,也不是最短循环节的倍数。

看明白了,请给我点赞,谢谢(*^▽^*)。

时间复杂度 O ( n ) \mathcal{O}(n) O(n)

KMP+线性扫描, O ( n ) \mathcal{O}(n) O(n)

C++ 代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6 + 10;
char s[N]; int ne[N];

int main(){
    int T = 1;
    int n;
    while(scanf("%d", &n), n){
        printf("Test case #%d\n", T ++);
        scanf("%s", s + 1);
        
        for(int i = 2, j = 0; i <= n; ++ i){
            while(j && s[i] != s[j + 1]) j = ne[j];
            if(s[i] == s[j + 1]) j ++;
            ne[i] = j;
        }
        
        for(int i = 1; i <= n; ++ i){
            int t = i - ne[i];
            if(i > t && i % t == 0){  // i>t保证循环节至少出现2次
                printf("%d %d\n", i, i / t);
            }
        }
        puts("");
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《算法艺术与信息学竞赛题解pdf》是一本介绍算法艺术和信息学竞赛题解的教材。它通过详细解析一系列典型的竞赛题目,讲解了如何使用不同的算法和数据结构来解决这些问题。 在这本教材中,作者首先介绍了算法和信息学竞赛的基本知识,包括常用的数据结构和算法思想。然后,他通过具体的例子和题目,展示了如何应用这些知识来解决实际问题。每个题目都有详细的解析过程,包括问题的分析、算法的设计和优化等内容。 这本教材的特点之一是注重实践。作者通过大量的实例和练习题,帮助读者巩固所学的知识,并掌握解决问题的方法。此外,他还提供了一些常见的竞赛技巧和经验,帮助读者在竞赛中取得好的成绩。 《算法艺术与信息学竞赛题解pdf》适合对算法和信息学竞赛感兴趣的读者。无论是初学者还是有一定基础的读者,都可以从中受益。通过学习这本教材,读者不仅可以提高解决问题的能力,还可以培养逻辑思维和计算机编程的能力。 总之,这本教材提供了一种全面的学习算法和信息学竞赛的方式。通过深入浅出的讲解和丰富的实例,它帮助读者建立起坚实的算法基础,提高解决问题的能力,并在竞赛中取得优异的成绩。 ### 回答2: 算法艺术与信息学竞赛题解pdf是一本以算法和信息学竞赛题为内容的电子书,提供了有关这些题目的详细解答。该书的出版旨在帮助读者更好地理解和掌握算法和信息学竞赛的核心知识和技巧。 首先,这本书介绍了一些常见的算法和数据结构,如贪心算法、动态规划、图论等。通过逐一解析题目,并给出相应的算法设计和实现思路,读者可以学习到不同类型题目的解题方法和技巧。 其次,该书还强调了对问题进行建模的重要性。在解决问题时,合理的问题建模可以将问题转化为更易于理解和求解的形式。书中通过具体的例子,教给读者如何抽象问题,构建合适的数据结构来解决实际问题。 此外,该书还提供了大量典型题目的详细解答,包括解题思路、具体实现和代码示例等。读者可以通过参考这些题目的解答,了解不同类型题目的解题思路,提高自己的解题能力。 总之,算法艺术与信息学竞赛题解pdf是一本帮助读者提高算法和信息学竞赛能力的实用电子书。通过学习其中的知识和技巧,读者可以更好地解决相关问题,并在竞赛中获得优异成绩。 ### 回答3: 《算法艺术与信息学竞赛题解PDF》是一本内容丰富的电子书,主要讲解了算法艺术和信息学竞赛中常见的题目解法。该书以清晰简洁的语言,详细介绍了解题思路和具体实现过程。 这本电子书中涵盖了多个题型,包括排列组合、图论、动态规划、贪心算法等。通过这些经典的题目,读者可以了解到不同算法在解决问题时的特点和应用场景,提升算法设计和编程能力。 该电子书特色之一是讲解了信息学竞赛中被广泛使用的算法和数据结构,如并查集、最短路径算法、网络流等。阅读该书可以让读者对这些常用的算法有更深入的理解,从而在解决实际问题时能够选择适当的算法。 此外,该电子书为了方便读者理解,还提供了大量的实例,以演示不同算法的具体应用。这些实例不仅帮助读者掌握算法的思维方式,还能够培养读者的问题分析和解决能力。 总之,《算法艺术与信息学竞赛题解PDF》是一本非常实用的电子书,适合对算法和信息学竞赛感兴趣的读者。通过阅读该书,读者可以提高解题速度和准确度,增强算法设计和编程能力,对解决问题的思路和方法有更深入的认识。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值