找到给定字符串的主要循环子字符串

时间:2013-09-13 18:27:40

标签: string algorithm language-agnostic substring cycle

我正在寻找一种算法来查找给定字符串的显性循环子字符串

循环子字符串

  • 一个子串,它相邻重复两次或多次。

显性循环子字符串

  • 具有最相邻重复的子串是显性的
  • (相邻重复次数相同)
    • 最长的子串是显性的
  • (关于长度和相邻重复的关系)
    • 首先出现的子字符串是

示例1:

  • prefixgarbagecyclecyclecyclecyclesufixgarbage
  • 返回cycle:=> cycle是重复最多的相邻子字符串

示例2:

  • prefixgarbagecyclepadinggarbagecyclesufixgarbage
  • 返回g:=> cycle的出现不会相邻重复,g相邻重复两次

示例3:

  • prefixgarbagecyclecyclepadinggarbageprefixgarbage
  • 返回cycle:=> cycle& g相邻重复两次,但cycleg
  • 更长

示例4:

  • prefixgarbagecyclecyclecycleroundroundroundprefixgarbage
  • 返回cycle:=> cycle& round重复三次&相同的长度,但cycle首先出现

例5:

  • abcdefghijklmnopqrstuvqxyz
  • 返回<empty string>,因为没有重复的相邻子字符串

实施此算法的最佳方法是什么?

4 个答案:

答案 0 :(得分:2)

找不到比这个二次时算法更好的东西(用Python实现):

IREP, IPER, IPOS = 0, 1, 2

def find_dominant(src):
  best = (0, 0, 0) # repetitions-1, period, position

  period = 0
  while period < len(src) // max(2, 1 + best[IREP]):
    period += 1
    length = 0

    for pos in range(len(src) - 1 - period, -1, -1):
      if src[pos] == src[pos + period]:
        length += 1
        repetitions = length // period
        if repetitions >= best[IREP]:
          best = (repetitions, period, pos)
      else:
        length = 0

  return best


s = "prefixgarbagecyclecyclecyclecyclesufixgarbage"
res = find_dominant(s)
if res[0] == 0:
  print("nothing found")
else:
  print(res[IREP] + 1, '*', s[res[IPOS]: res[IPOS] + res[IPER]])

对于每个可能的周期,扫描字符串并记住最长的周期性子序列。向后扫描以检查较少的条件。当没有找到进一步的改善时,停止增加期限。

时间复杂度为O(N 2 / R),其中R是主要子串的重复次数。空间复杂度为O(1)。

答案 1 :(得分:1)

这是一种可行的方法(由于您的周期必须相邻,因此更简单)。选一个字符串。看看它是否重复。跟踪最重复的一个。

编辑实际测试的Python代码:

testStrings =[ "prefixgarbagecyclecyclecyclecyclesufixgarbage",
               "prefixgarbagecyclepadinggarbagecyclesufixgarbage",
               "prefixgarbagecyclecyclepadinggarbageprefixgarbage",
               "prefixgarbagecyclecyclecycleroundroundroundprefixgarbage",
               "abcdefghijklmnopqrstuvqxyz"];

for input in testStrings:

 repCountMax = 0
 longestCycle = ""
 repCount = 0
 for i in range (1, len(input)):
  for j in range( i+1, len(input)):
    substring = input[i:j]
    #print substring
    ls = len(substring)
    repCount = 1
    k = j
    while(substring == input[k:k+ls]):
      k = k + ls
      repCount = repCount +1
      #print "repetition ", repCount, " of ", substring, "\n"
    if (repCount > repCountMax) or ((repCount == repCountMax) and len(substring) > len(bestCycle)):
      repCountMax = repCount
      bestCycle = substring

 if repCountMax > 1:
  print "best cycle in '", input, "' is '", bestCycle,"' which is repeated ", repCountMax, " times."
 else:
  print "no repeated cycles found in string ", input

结果输出:

  'prefixgarbagecyclecyclecyclecyclesufixgarbage'中的最佳循环是'   ecycl'重复4次。

     

最好的周期'   prefixgarbagecyclepadinggarbagecyclesufixgarbage'是'g',它是   重复2次。

     

最好的周期'   prefixgarbagecyclecyclepadinggarbageprefixgarbage'是'ecycl'   重复2次。

     

最好的周期'   prefixgarbagecyclecyclecycleroundroundroundprefixgarbage'是'ecycl'   重复3次。

     

在字符串中找不到重复的循环   abcdefghijklmnopqrstuvqxyz

注意 - 找到的周期为ecycl,而不是cycleecycl首先发生了......

第二个注意事项 - 当你不能再“击败”当前的最佳估计值时,你可以通过停止来提高效率 - 例如,如果你已经找到了5次重复,并且给出了你要搜索的字符串的大小没有六次重复的空间。当存在大量重复时,这将提高速度。请参阅Evgeny的解决方案,了解实现该方法的方法。

答案 2 :(得分:1)

从左向右扫描。

您需要一些键/值对。关键是一封信。该值包括扫描中找到该字母的最后一个字母实例的索引以及该字母属于两个或多个字符串所属的任何循环的信息(最后一个以该列中该字母开头)。

您需要一个地方来存储有关找到的任何周期的信息。称之为“自行车商店”。

扫描时,请在每个索引处执行此操作:

  • 在那里使用这封信。看看它是否在键表中。
  • 如果找到,请查看以下字母是否与表格中找到的字母(上一次出现)和此字母(当前扫描索引)之间的字母匹配。
  • 如果它们匹配,我们有一个循环,看看前一次出现的信息是否显示这是存储的循环信息的延续。 (注意:相邻字母可能是特殊情况。)
  • 如果是延续,
    • 添加到存储的周期信息以包含这些字符
    • 更新此信件最后一次出现的索引
    • 在周期商店中更新有关此周期的信息
  • 如果不是延续
    • 创建(或替换)存储的周期信息以显示此新周期(count = 2)
    • 更新此信件最后一次出现的索引
    • 在周期商店中添加有关此周期的信息
  • 如果此次出现后的字母与该次出现后的字母不匹配,
    • 删除此信件的所有存储周期信息
    • 更新此信件最后一次出现的索引
  • 如果表格中没有该字母,请为此字母和此索引添加键/值对。

完成扫描后,查看循环存储以查看哪一个占优势。

请注意,您可能不必将所有周期存储到结束,但对我来说并不是很明显,如何确定哪些周期可以丢弃。可能是基于键/值对表的内容以及到目前为止的主导值保持仍然可能继续的那些。

答案 3 :(得分:0)

我认为可以在这里应用KMP算法的修改形式。

从头开始遍历字符串。

拿第一个字母,存放它。拿下一个字母,如果它相同,则有2个候选者重复循环,否则将其添加到现有字符串。

基本上,在每封信中,您都会有一份可能的重复列表子列表。当然,你必须记录长度和最大值。至此为止的重复。

如果我没有弄错的话,这可以在O(n)时间内完成。