【问题标题】:Cycle detection in bit-string [duplicate]位串中的循环检测[重复]
【发布时间】:2015-03-14 10:21:48
【问题描述】:

给定一个长度为 N(最多 1000 至少 1

例子:

110110110110 循环长度为3(模式重复为110)

000000 循环长度为1(模式重复为0)

1101101101 循环长度为3(模式重复为110)

我试图理解Floyd's cycle detection algorithm,但我无法理解如何申请这个问题。

如何有效地解决这个问题? (我想要一个运行在 O(NlogN) 或更好的算法)。

【问题讨论】:

  • 取决于您如何定义问题。 1101101101的周期是3吗?或者不是因为最后一个周期没有完成? (110 110 110 1)
  • ^是的。我将编辑问题
  • 能否请您发布您的问题的原始陈述? (我敢肯定是来自一些编程比赛或一些在线评委)

标签: algorithm cycle bitstring


【解决方案1】:

这是这个问题的线性解决方案:

  1. 让我们计算给定字符串的前缀函数(就像在 Knuth-Morris-Pratt 的算法中一样)。

  2. 答案总是n - p[n],其中n 是给定字符串的长度,p[i] 是字符串中i-th 位置的前缀函数值。证明:

    • 周期不小于n - p[n]。之所以如此,是因为对于任何时期 ks[i] = s[i + k] 对于任何 i。因此,由于前缀函数的定义,n - p[n] 至少是k

    • 周期不大于k = n - p[n]。之所以如此,是因为s[i] = s[i + k]对于所有i由于前缀函数的定义,这意味着k是一个句号。

【讨论】:

  • 您的算法是否有任何正确性证明?实际上我不只是想要答案,我实际上想了解您是如何想出它的! :)
  • @user3918495 我已经添加了证明。
  • @kaktusito 当循环未完成时它确实有效。对于1101101101,在最后一个位置结束并且是给定字符串前缀的最长字符串是1101101,这意味着n - p[n] = 3
  • 是的,我意识到这一点并删除了我的评论。 +1
  • @Rup 前缀函数定义在任何字母表上。不管是01还是字母都没有关系。
【解决方案2】:

Floyd 的循环检测算法被认为用于稍微不同的问题,即图,其中存在循环,但并非整个图都必须是循环。

例如,比较这两个图表:

1 -> 2 -> 3 -> 4 -> 1 -> ...

1 -> 2 -> 3 -> 4 -> 2 -> ...

两者都有一个循环,但第二个只有部分节点上有一个循环(即循环中没有出现1)。

您对示例 2 中的循环不感兴趣,只对“完整循环”感兴趣。


此外,当您使用位时,您的算法将与使用整数时略有不同(例如)。原因是您可以一次比较多个位,而只进行一次比较(只要总位数

以下是您如何解决问题的可能想法:

检查是否有1的循环,将整数移一位,并与自身比较:

000000000000
 000000000000
-yyyyyyyyyyy-
=> Matches!

110110110110
>110110110110
-ynnynnynnyn-
=> Nope

所以000000000000 的循环是 1,110110110110 没有,所以继续用 2 测试:

110110110110
>>110110110110
--nynnynnynn--
=> Nope

继续 3:

110110110110
>>>110110110110
---yyyyyyyyy---
=> Matches!

当然,您必须使用位算术来实现我刚才描述的内容,这由您决定。

【讨论】:

  • 您描述的算法由于位级算术而节省了一个常数因子,但渐近不是 O(N lg N) 或更好(假设任意循环 len),正如 OP 所希望的那样。上面的评论@MBo 链接到了这个问题的有效解决方案。
  • 我刚刚看到我忽略了问题中给出的尺寸要求,所以是的,这个答案并不正确......
  • 是的,但在最坏的情况下,我会对长度为 N 的字符串进行 K 次比较,因此复杂度将是 O(NK),效率不如我想要的。
  • @kaktusito MBo的解决方案不就是和这个一样吗?
  • 我真的不明白你想如何使用散列来解决问题...对输入进行散列并将其与什么进行比较?
【解决方案3】:

当它总是从第一个周期开始时,它会很简单。你可以这样做:

public int GetCycleLength(string binary, out int cycles)
{
    for (int i = 1; i < 1000; i++)
    {
        if (binary.Length % i == 0)
        {
            cycles = 0;
            do
            {
                cycles++;
                if (cycles * i > binary.Length - i - 1)
                {
                    break;
                }
            }
            while (binary.Substring(cycles * i, i) == binary.Substring((cycles + 1) * i, i));
            cycles++;
            if (cycles * i == binary.Length)
            {
                return i;
            }
        }
    }
    cycles = 0;
    return 0;
}

【讨论】:

    【解决方案4】:

    除了我已有的答案,我还有一个想法。也许它根本不起作用,所以如果我错了请纠正我(我在谷歌上找不到关于这个主题的任何信息)。但是,它不适用于位级别,因此您的开销为 32 或 64...

    我们将正在分析的字符串称为S

    您也许可以使用 Knuth-Morris-Pratt 算法(以线性时间在字符串中查找子字符串)在字符串 SS 中查找 S(将 S 连接两次)。当然,您必须从索引 2 开始搜索。然后算法返回的索引就是循环的长度。


    编辑:正如 kaktusito 所说,这行不通。但是,您可以使用 KMP 算法(但稍作修改)从索引 2 开始的字符串 S 中查找字符串 S。原始算法当然不会找到匹配项,但您可以对其进行修改,以继续搜索(即使您要查找的子字符串比原始字符串长)。然后,只要将子字符串匹配到原始字符串的末尾,您就会找到一个循环的长度(即使子字符串更长)。

    【讨论】:

    • 考虑到 OP 说 1101101101 有循环 len 3(即使循环未“完成”),我不知道这将如何工作。
    • 啊,你是对的!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-06
    • 1970-01-01
    • 2016-12-15
    • 2018-02-06
    • 2011-04-26
    • 1970-01-01
    • 2016-04-17
    相关资源
    最近更新 更多