【问题标题】:How to find the period of a string如何找到字符串的句点
【发布时间】:2014-02-05 18:55:37
【问题描述】:

我从用户那里得到一个输入,它是一个带有某个子字符串的字符串,该子字符串在整个字符串中重复。我需要输出子字符串或其长度 AKA 句点。

S1 = AAAA // substring is A
S2 = ABAB // Substring is AB
S3 = ABCAB // Substring is ABC
S4 = EFIEFI // Substring is EFI

我可以从单个字符开始并检查它是否与下一个字符相同,如果不是,我可以用两个字符然后用三个等等。这将是一个 O(N^2) 算法。我想知道是否有更优雅的解决方案。

【问题讨论】:

  • 为什么 S2 子串不是 AB?
  • 现在我对 S3 感到困惑。 ABC 不重复,或者这也是一个错字?对不起,我很挑剔,我只是想弄清楚你想要输出什么。
  • 嗯,输入字符串中的每个字符都是重复子字符串的一部分。字符串长度是多少无关紧要。 @csmckelvey
  • 对于字符串 "abababxxxxabababxxxx",龟兔赛跑给你周期 2,而实际周期是 10。

标签: string algorithm substring


【解决方案1】:

您可以通过归纳计算字符串的每个前缀的周期,在线性时间和恒定的附加空间中做到这一点。我不记得细节了(有几件事要弄好),但你可以在Section 13.6 of "Text algorithms" by Crochemore and Rytter under function Per(x)找到它们。

【讨论】:

  • +1 供参考。该算法确实存在,第 340 页。不过看起来不像是面试解决方案 :)
  • 对勘误表的引用也会有所帮助,因为子例程 Next_MS 中至少有三个错误,Per 中至少还有两个错误。
  • @PeterTaylor:如果您知道这些错误,您能澄清一下它们是什么吗?对旧问题的旧答案发表评论说答案有错误,但不知道这些错误可能是什么,这有点帮助,但也非常令人沮丧。
  • @R,简单看一下文字,在Next_MS的签名中我怀疑x[1…m]应该是x[1...n](因为m没有被使用,n没有定义使用); then end 几乎可以肯定是 then beginms := j; j := ms+1; 看起来很可疑;在Per 中有一个then 可能应该是then begin 和一个else end 可能应该是else begin。但我不确定这些是我 15 个月前看到的错误,而且官方勘误表无论如何都比我的意见更有价值。
【解决方案2】:

我也一直在寻找这个问题的时空最优解决方案。 accepted answer by tmyklebu 本质上似乎就是它,但我想对它的实际含义和一些进一步的发现提供一些解释。

首先,我提出的这个问题提出了一个看似有希望但不正确的解决方案,并说明了为什么它不正确:Is this algorithm correct for finding period of a string?

一般来说,“找到句点”的问题等同于“在其自身中找到模式”(在某种意义上,“strstr(x+1,x)”),但没有匹配结束的约束。这意味着您可以通过采用 任何从左到右 字符串匹配算法并将其应用于自身,将到达干草堆/文本末尾的部分匹配视为匹配来找到句点,并且时间和空间要求与您使用的任何字符串匹配算法相同。

tmyklebu 的回答中引用的方法本质上是将这一原则应用于String Matching on Ordered Alphabets,也适用于explained here。使用GS algorithm 应该可以实现另一个时空优化解决方案。

不幸的是,众所周知且简单的Two Way 算法(也称为explained here不是解决方案,因为它不是从左到右的。特别地,左因子失配后的推进取决于右因子已经匹配,并且不可能与右因子不对齐的另一场匹配以右因子的周期为模。当在其自身中搜索模式并忽略结束后的任何内容时,我们无法得出任何关于下一次右因子匹配可能发生多快的结论(部分或全部右因子可能已经移动到模式的末尾),因此无法进行保持线性时间的移位。

当然,如果工作空间可用,则可以使用许多其他算法。 KMP 是具有 O(n) 空间的线性时间,并且可以将其调整为仅具有对数空间的仍然相当有效的东西。

【讨论】:

    【解决方案3】:

    让我假设字符串n 的长度至少是句点p 的两倍。

    算法

    1. m = 1,S 整个字符串
    2. m = m*2
      • 查找下一个出现的子字符串 S[:m]
      • k 成为下一个事件的开始
      • 检查 S[:k] 是否是句号
      • 如果没有,请转到 2.

    示例

    假设我们有一个字符串

    CDCDFBFCDCDFDFCDCDFBFCDCDFDFCDC
    

    对于 2 的每个幂 m,我们发现第一个 2^m 字符的重复。然后我们将此序列扩展到它的第二次出现。让我们从 2^1 开始,所以CD

    CDCDFBFCDCDFDFCDCDFBFCDCDFDFCDC
    CDCD   CDCD   CDCD   CDCD   CD
    

    我们不扩展CD,因为下一次出现就在那之后。但是 CD 不是我们要寻找的子字符串,所以让我们使用下一个幂:2^2 = 4 和子字符串 CDCD

    CDCDFBFCDCDFDFCDCDFBFCDCDFDFCDC
    CDCD   CDCD
    

    现在让我们将字符串扩展到第一次重复。我们得到

    CDCDFBF
    

    我们检查这是否是周期性的。事实并非如此,我们走得更远。我们尝试 2^3 = 8,所以CDCDFBFC

    CDCDFBFCDCDFDFCDCDFBFCDCDFDFCDC
    CDCDFBFC      CDCDFBFC      
    

    我们尝试扩展并得到

    CDCDFBFCDCDFDF
    

    这确实是我们的时代。

    我希望这可以在 O(n log n) 中使用一些类似 KMP 的算法来检查给定字符串的出现位置。请注意,此处仍应解决一些边缘情况。

    直觉上这应该可行,但是我的直觉已经在这个问题上失败了一次,所以如果我错了,请纠正我。我会试着找出一个证据。

    虽然是一个非常好的问题。

    【讨论】:

    • KMP 预处理的重点在于它计算后缀-前缀匹配。您可以从故障表的最后一个条目中读取字符串的句点。
    • 嗯,这里我的意思是 KMP 是一种 O(n) 算法,用于在字符串中查找模式。
    【解决方案4】:

    你可以在线性时间内为整个字符串构建一个后缀树(后缀树在网上很容易查到),然后递归计算并存储后缀树的叶子数(后缀前缀的出现次数)N(v)在后缀树的每个内部节点 v 下方。还递归地计算并存储树的每个节点处每个后缀前缀 L(v) 的长度。然后,在树中的内部节点 v 处,在 v 处编码的后缀前缀是一个重复的子序列,如果 N(v) 等于字符串的总长度除以 L(v),则该子序列会生成您的字符串。

    【讨论】:

    • 你当然可以用后缀树来做这件事,但这会比做 KMP 预处理慢几个数量级。
    • @tmyklebu 你怎么知道后缀树的常数比其他算法大几个数量级?这意味着常数为 1000。后缀树当然没有隐藏常数 1000。
    • 我实际上还没有看到没有的实现。欢迎您将我链接到您最喜欢的实现,我可以与我引用的线性时间、恒定空间算法的良好实现进行比较,但是后缀树慢 1000 倍真的不会让我感到惊讶。跨度>
    【解决方案5】:

    如果输入字符串中的每个字符都是重复子字符串的一部分,那么您所要做的就是存储第一个字符并将其与字符串的其余字符逐个比较。如果找到匹配项,直到匹配的字符串是您的重复字符串。

    【讨论】:

    • 您能否展示一个代码示例,说明这对于几个输入是如何工作的?
    • 老兄。阅读我的问题中示例下方的文本段落:|
    • 哦,我没看到。但是由于您说它必须包含一个循环,因此该方法的复杂性将是 O(N),因为您只是在最坏的情况下将第一个字符与整个字符串进行比较。 @csmckelvey 伪代码将是这样的:char first = input[0]; String repeatingString = first; int i = 1; char nextChar = input[i]; while(first!=nextChar) { repeatingString += nextChar; i++; nextChar = input[i]; }
    • 在最坏的情况下它的 O(N^2) 会导致它首先检查 1 个字符然后 2 个字符而不是 3 .. 直到 N/2 所以它是 1+2+3+..N /2 => (N/2)(N/2+1)/2 删除你拥有的所有常量 N^2
    • @Aditya 不,使用上述方法,它将检查第一个字符和第二个字符,然后是第三个字符,依此类推。所以它将是 1+1+1 .. 在最坏的情况下,它将是 N(输入长度​​)乘以字符比较。您不是将 A 与 AB 进行比较,然后将 A 与 ABC 进行比较。您将 A 与 B 进行比较,然后将 A 与 C 进行比较。所以它将是 O(N)。只要看看我提供的上面的伪代码。它将最大循环输入字符串的长度。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    • 1970-01-01
    • 2016-09-14
    相关资源
    最近更新 更多