今天,我的一个朋友在面试软件开发人员的职位时被问到以下问题:

给定两个字符串s1和s2,你将如何检查s1是否是s2的旋转版本?

例子:

如果s1 = "stackoverflow",则以下是它的一些旋转版本:

"tackoverflows"
"ackoverflowst"
"overflowstack"

其中“stackoverflwo”不是一个旋转的版本。

他给出的答案是:

取s2,找出s1的子字符串中最长的前缀,就能得到旋转的点。一旦你找到了那个点,在那个点打断s2得到s2a和s2b,然后检查是否连接(s2a,s2b) == s1

对我和我的朋友来说,这是一个很好的解决方案。但是面试官不这么认为。他要求一个更简单的解决办法。请告诉我在Java/C/ c++中你是如何做到这一点的?

提前谢谢你。


当前回答

Opera的简单指针旋转技巧是有效的,但是在运行时最糟糕的情况下效率极低。简单地想象一个有许多长重复字符的字符串,例如:

S1 = HELLOHELLOHELLO1HELLOHELLOHELLO2 S2 = HELLOHELLOHELLO2HELLOHELLOHELLO1

从计算角度来看,“循环直到出现不匹配,然后再加1,再试一次”是一种可怕的方法。

为了证明你可以毫不费力地在C语言中使用串联方法,下面是我的解决方案:

  int isRotation(const char* s1, const char* s2) {
        assert(s1 && s2);

        size_t s1Len = strlen(s1);

        if (s1Len != strlen(s2)) return 0;

        char s1SelfConcat[ 2 * s1Len + 1 ];

        sprintf(s1SelfConcat, "%s%s", s1, s1);   

        return (strstr(s1SelfConcat, s2) ? 1 : 0);
}

这在运行时间上是线性的,以开销中O(n)内存使用为代价。

(请注意,strstr()的实现是特定于平台的,但如果特别脑死亡,总是可以用更快的替代方案替代,如Boyer-Moore算法)

其他回答

我喜欢检查s2是否是s1与s1连接的子字符串的答案。

我想要添加一个不会失去其优雅的优化。

你可以使用连接视图代替连接字符串(我不知道其他语言,但c++ Boost。Range提供这样的视图)。

由于检查一个字符串是否是另一个字符串的子字符串具有线性平均复杂度(最坏情况的复杂度是二次的),这种优化应该平均提高2倍的速度。

Opera的简单指针旋转技巧是有效的,但是在运行时最糟糕的情况下效率极低。简单地想象一个有许多长重复字符的字符串,例如:

S1 = HELLOHELLOHELLO1HELLOHELLOHELLO2 S2 = HELLOHELLOHELLO2HELLOHELLOHELLO1

从计算角度来看,“循环直到出现不匹配,然后再加1,再试一次”是一种可怕的方法。

为了证明你可以毫不费力地在C语言中使用串联方法,下面是我的解决方案:

  int isRotation(const char* s1, const char* s2) {
        assert(s1 && s2);

        size_t s1Len = strlen(s1);

        if (s1Len != strlen(s2)) return 0;

        char s1SelfConcat[ 2 * s1Len + 1 ];

        sprintf(s1SelfConcat, "%s%s", s1, s1);   

        return (strstr(s1SelfConcat, s2) ? 1 : 0);
}

这在运行时间上是线性的,以开销中O(n)内存使用为代价。

(请注意,strstr()的实现是特定于平台的,但如果特别脑死亡,总是可以用更快的替代方案替代,如Boyer-Moore算法)

int rotation(char *s1,char *s2)
{
    int i,j,k,p=0,n;
    n=strlen(s1);
    k=strlen(s2);
    if (n!=k)
        return 0;
    for (i=0;i<n;i++)
    {
        if (s1[0]==s2[i])
        {
            for (j=i,k=0;k<n;k++,j++)
            {
                if (s1[k]==s2[j])
                    p++;
                if (j==n-1)
                    j=0;
            }
        }
    }
    if (n==p+1)
      return 1;
    else
      return 0;
}

这是一个O(n) and in place算法。它对字符串的元素使用<操作符。当然不是我的。我把它从这里(网站是波兰语。我以前偶然发现过一次,现在在英语中找不到这样的东西,所以我展示了我拥有的:))。

bool equiv_cyc(const string &u, const string &v)
{
    int n = u.length(), i = -1, j = -1, k;
    if (n != v.length()) return false;

    while( i<n-1 && j<n-1 )
    {
        k = 1;
        while(k<=n && u[(i+k)%n]==v[(j+k)%n]) k++;
        if (k>n) return true;
        if (u[(i+k)%n] > v[(j+k)%n]) i += k; else j += k;
    }
    return false;
}

另一个python示例(基于答案):

def isrotation(s1,s2):
     return len(s1)==len(s2) and s1 in 2*s2