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

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

例子:

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

"tackoverflows"
"ackoverflowst"
"overflowstack"

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

他给出的答案是:

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

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

提前谢谢你。


当前回答

现在来点完全不同的东西。

如果你想在一些约束条件下快速得到答案当字符串不是相互旋转时

在两个字符串上计算一些基于字符的校验和(比如xoring所有字符)。如果签名不同,字符串不是彼此的旋转。

同意,它可能会失败,但如果字符串不匹配,它会很快说,如果它们匹配,你仍然可以使用另一种算法,比如字符串连接来检查。

其他回答

下面是一个使用正则表达式的例子,只是为了好玩:

boolean isRotation(String s1, String s2) {
   return (s1.length() == s2.length()) && (s1 + s2).matches("(.*)(.*)\\2\\1");
}

如果可以使用保证不在任何字符串中出现的特殊分隔符,可以使它更简单一些。

boolean isRotation(String s1, String s2) {
   // neither string can contain "="
   return (s1 + "=" + s2).matches("(.*)(.*)=\\2\\1");
}

你也可以使用有限重复的回溯:

boolean isRotation(String s1, String s2) {
   return (s1 + s2).matches(
      String.format("(.*)(.*)(?<=^.{%d})\\2\\1", s1.length())
   );
}

首先确保s1和s2的长度相同。然后检查s2是否是s1与s1连接的子字符串:

algorithm checkRotation(string s1, string s2) 
  if( len(s1) != len(s2))
    return false
  if( substring(s2,concat(s1,s1))
    return true
  return false
end

在Java中:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && ((s1+s1).indexOf(s2) != -1);
}

现在来点完全不同的东西。

如果你想在一些约束条件下快速得到答案当字符串不是相互旋转时

在两个字符串上计算一些基于字符的校验和(比如xoring所有字符)。如果签名不同,字符串不是彼此的旋转。

同意,它可能会失败,但如果字符串不匹配,它会很快说,如果它们匹配,你仍然可以使用另一种算法,比如字符串连接来检查。

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算法)

由于其他人已经提交了二次最坏情况时间复杂度解决方案,我将添加一个线性解决方案(基于KMP算法):

bool is_rotation(const string& str1, const string& str2)
{
  if(str1.size()!=str2.size())
    return false;

  vector<size_t> prefixes(str1.size(), 0);
  for(size_t i=1, j=0; i<str1.size(); i++) {
    while(j>0 && str1[i]!=str1[j])
      j=prefixes[j-1];
    if(str1[i]==str1[j]) j++;
    prefixes[i]=j;
  }

  size_t i=0, j=0;
  for(; i<str2.size(); i++) {
    while(j>0 && str2[i]!=str1[j])
      j=prefixes[j-1];
    if(str2[i]==str1[j]) j++;
  }
  for(i=0; i<str2.size(); i++) {
    if(j>=str1.size()) return true;
    while(j>0 && str2[i]!=str1[j])
      j=prefixes[j-1];
    if(str2[i]==str1[j]) j++;
  }

  return false;
}

工作示例