Main-Lorentz 算法
重串
定义
给定一个长度为
我们将一个字符串连续写两遍所产生的新字符串称为 重串 (tandem repetition)。下文中,为了表述精准,我们将被重复的这个字符串称为原串。换言之,一个重串等价于一对下标
你的目标是找出给定的字符串
下文的算法由 Michael Main 和 Richard J. Lorentz 在 1982 年提出。
声明:
下文所有的字符串下标从 0 开始。
下文中,记
解释
考虑字符串
下面是另一个例子,考虑字符串
重串的个数
一个长度为
但这并不影响我们在
这里有一些关于重串数量的有趣结论:
- 如果一个重串的原串不是重串,则我们称这个重串为 本原重串 (primitive repetition)。可以证明,本原重串最多有
个。 - 如果我们把一个重串用 Crochemore 三元组
进行压缩,其中 是重串的起始位置, 是该重串某个循环节的长度(注意不是原串长度!), 为这个循环节重复的次数。则某字符串的所有重串可以被 个 Crochemore 三元组表示。 - Fibonacci 字符串定义如下:
可以发现 Fibonacci 字符串具有高度的周期性。对于长度为
Main-Lorentz 算法
解释
Main-Lorentz 算法的核心思想是 分治。
这个算法将字符串分为左、右两部分,首先计算完全处于字符串左部(或右部)的重串数量,然后计算起始位置在左部,终止在右部的重串数量。(下文中,我们将这种重串称为 交叉重串)
计算交叉重串的数量是 Main-Lorentz 算法的关键点,我们将在下文详细探讨。
过程
寻找交叉重串
我们记某字符串的左部为
对于任意一个重串,我们考虑其中间字符。此处我们将一个重串右半边的第一个字符称为其中间字符,换言之,若
接下来,我们将会探讨如何找到所有的左偏重串。
我们记一个左边重串的长度为
我们考虑固定
显然,我们一旦固定了
左偏重串的判定
即使固定
我们再来举一个例子,对于字符串
于是,我们可以给出某个长度为
记
总结一下,即有:
- 固定一个
。 - 那么我们此时要找的重串长度均为
。此时可能仍有多个符合条件的重串,取决于 与 的取值。 - 计算上文提到的
, 。 - 则所有符合条件的重串符合条件:
接下来,只需要考虑如何快速算出
- 计算
:只需计算 的 Z 函数即可。 - 计算
:只需计算 的 Z 函数即可,其中 是一个 , 中均没有的字符。
右偏重串
计算右偏重串的方法与计算左偏重串的方法几乎一致。考虑该重串第一个落入
令
枚举
实现
Main-Lorentz 算法以四元组
请注意,如果你想通过这些四元组来找到所有重串的起始位置与终止位置,则最坏时间复杂度会达到 repetitions
中。
本页面主要译自博文 Поиск всех тандемных повторов в строке. Алгоритм Мейна-Лоренца 与其英文翻译版 Finding repetitions。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。
贡献者:@Yufan@WenzelTian@fsy-juruo
本页面最近更新:2/3/2023, 12:00:00 AM,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用