在O(n)时间内确定字符串的旋转次数与原始字符串的匹配程度?

时间:2013-05-02 23:15:51

标签: performance algorithm rotation

有很多问题讨论旋转字符串的最快/最佳方法,或确定一个字符串是否是另一个字符串的旋转。

例如,忽略输入的清理,您会看到IsRotation的something like this

public static bool IsRotation(string s1, string s2)
{
    return (s1.Length == s2.Length && (s1 + s1).IndexOf(s2) != -1);
}

对于Rotate来说是这样的:

public static string Rotate(string s, int index)
{
    //Can't rotate.  Return input.
    if (s.Length < 2)
    {
        return s;
    }

    // Break input in half at rotation index
    var s1 = s.Substring(0, index);
    var s2 = s.Substring(index);

    // Reverse the halves
    var s1Reversed = Reverse(s1);
    var s2Reversed = Reverse(s2);

    // Join the reversed halves
    var joined = s1Reversed + s2Reversed;

    //Reverse and return the result.
    var rotated = Reverse(joined);
    return rotated;
}

例如,使用“foo ...” 旋转(“foo”,1)==“ofo” -和- IsRotation(“foo”,“ofo”)== true;

我的问题是这些问题的延伸。

给定一个输入字符串s,确定该字符串与原始字符串的旋转次数。

将输入字符串视为旋转,一些示例输入/输出对:

IdenticalRotationCount("") == 1
IdenticalRotationCount("s") == 1
IdenticalRotationCount("sa") == 1
IdenticalRotationCount("ss") == 2
IdenticalRotationCount("ByeBye") == 2
IdenticalRotationCount("StackOverflow") == 0

我被告知有一个解决方案将在O(n)时间内运行。初学者解决方案如下所示:

public static int IdenticalRotationCount(this string s)
{
    //If length of s is less than two, we cannot rotate.  Return 1.
    if (s.Length < 2)
    {
        return 1;
    }

    //Get first char in s
    var first = s[0];

    //Consider input as first rotation that matches
    var count = 1;

    //Try each rotate position
    for (var i = 1; i < s.Length; ++i)
    {
        var c = s[i];

        //If current character doesn't start with same character as input
        //we can skip the rotation
        if (c != first)
        {
            continue;
        }

        //If the rotation at index i equals the input string, add 1 to result
        if (StringExtensions.Rotate(s, i) == s)
        {
            ++count;
        }
    }

    return count;
}

但是,如果你选择一个荒谬的输入,比如连续20万个'a',它会运行很长一段时间。

任何人都可以提供在O(n)时间内运行的解决方案吗?我可以通过在旋转前将输入分解为两半而不是进行实际旋转来进行实际比较来看N ^ 2,但是看不到如何做O(n)。

谢谢!

PS - 如果有更适合发布此类问题,请在评论中说明。我很乐意搬家。

2 个答案:

答案 0 :(得分:0)

这让人想到 - 想一想这个问题“如果我将原始字符串与自身连接起来,那么连接结果中原始字符串的第一个索引是什么”。经过一番思考,它看起来好像它会回答问题O(n)。

E.g。 原始字符串“ByeBye” 连接字符串“ByeByeByeBye”

原始字符串出现在连接字符串中的(从0开始)索引2处。这告诉你一些事情。

答案 1 :(得分:0)

如果一个字符串等于它在偏移k处的旋转并且没有更小的偏移量,那么该字符串必须是其长度k前缀的重复。然后,它将在k的所有倍数处等于其旋转,因此将精确地n/k这样的旋转,其中n是字符串的长度。这很容易证明。

确实有O(n)算法用于确定k的值。一种方法是使用Duval算法进行Lyndon分解(参见Wikipedia)。那只是一个暗示;如果需要,我会看看是否可以产生一些实际的伪代码。