面试问题:检查一个字符串是否是其他字符串的旋转

Web*_*dev 235 c c++ java

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

鉴于两个字符串s1,s2您将如何检查是否s1旋转版本s2

例:

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

"tackoverflows"
"ackoverflowst"
"overflowstack"
Run Code Online (Sandbox Code Playgroud)

其中,作为"stackoverflwo"旋转的版本.

他给出的答案是:

获取s2并找到作为子字符串的最长前缀,s1它将为您提供旋转点.一旦你找到那个点,突破s2该点处得到s2as2b,然后就检查concatenate(s2a,s2b) == s1

对我和我的朋友来说,这似乎是一个很好的解决方案.但面试官不这么认为.他要求一个更简单的解决方案.请告诉我你将如何做到这一点来帮助我Java/C/C++

提前致谢.

cod*_*ict 687

首先确保s1并且s2长度相同.然后检查是否s2s1连接的子字符串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
Run Code Online (Sandbox Code Playgroud)

在Java中:

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

  • 我喜欢它的优雅,但我不得不考虑一下,检查没有任何误报.(我不认为*有.) (49认同)
  • 你也可以在Java中使用`(s1 + s1).contains(s2)`. (6认同)
  • @unicornaddict - 这个解决方案有什么好处,一旦你指出它就很明显,我讨厌自己没有想到它! (6认同)
  • @Jon专注于`s1 + s1`.很明显,它的所有大小为`s1.length`的子串都是`s1`的旋转,通过构造.因此,任何大小为`s1.length`的字符串都是`s1 + s1`的子字符串必须是`s1`的旋转. (5认同)
  • 无论如何,我会反对这一点作为面试问题.它有一个"啊哈!" 组件,我想.大多数程序员(包括我在内)只会使用蛮力,这无论如何都不是不合理的,并且对于面试官来说可能感觉不够"聪明". (4认同)
  • +1:一方面我很高兴,这正是我会回答这个问题的方式,另一方面我觉得3k + rep的想法有点不适我可以收获,如果我第一次出现在现场.干得好unicornaddict :) (3认同)
  • @Jon,@ codaddict,如果相同的字符串不被视为轮换,则此页面上的所有解决方案都支持误报.询问面试官,以及你是否受到记忆的限制,这将是一个很好的问题. (2认同)
  • @ MK1:这些面试问题通常用于检查此人如何解决问题. (2认同)
  • +1,很好的答案.可以在不分配新内存的情况下完成吗? (2认同)

Chr*_*ght 101

当然,一个更好的答案是,"好吧,我会问堆栈流社区,并且可能在5分钟内至少有4个非常好的答案".大脑是好的,但是我会更加重视那些知道如何与他人合作以获得解决方案的人.

  • 在面试过程中掏出你的手机可能会被认为是粗鲁的,最终他们最终会雇佣Jon Skeet. (51认同)
  • 纯粹的脸颊+1.让我的一天:-) (14认同)
  • 我不认为他们能买得起Jon Skeet. (6认同)
  • 如果他们不同意,您可以将他们链接到这个问题. (5认同)
  • 这实际上可能正是我所说的 (2认同)

Fed*_*oni 49

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

def isrotation(s1,s2):
     return len(s1)==len(s2) and s1 in 2*s2
Run Code Online (Sandbox Code Playgroud)

  • @Duncan:`in`运算符不使用O(n)算法吗? (2认同)

jpa*_*cek 32

由于其他人提交了二次最坏情况时间复杂度解决方案,我会添加一个线性的(基于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;
}
Run Code Online (Sandbox Code Playgroud)

工作实例

  • 为ideone.com +1 - 它看起来很有趣! (5认同)

Jon*_*eet 25

编辑:如果你发现,接受的答案显然比这更优雅和有效.如果我没有想到将原始字符串加倍,我就把这个答案留给了我.


我只是暴力强迫它.首先检查长度,然后尝试每个可能的旋转偏移.如果它们都没有成功,则返回false - 如果其中任何一个成功,则立即返回true.

没有特别需要连接 - 只需使用指针(C)或索引(Java)并同时走,每个字符串一个 - 从一个字符串的开头开始,第二个字符串中的当前候选旋转偏移量,并在必要时包装.检查字符串中每个点的字符相等性.如果你到达第一个字符串的末尾,那就完成了.

它可能很容易连接 - 尽管可能效率较低,至少在Java中是这样.

  • @Jon这是个玩笑!面试官没有采访Jon Skeet,Jon Skeet采访了他. (37认同)
  • +1 - 我们不需要没有优雅的解决方案,其运行效率是最有效的解决方案的3倍以上.这是C ...微优化是*de riguer*. (8认同)
  • 采访者:洛塔谈话,但我打赌这家伙不能代码. (8认同)
  • @Beau:如果有人想这么想,欢迎他们问我代码.如果有人问我"我将如何做某事",我通常会描述算法,而不是跳跃到代码. (8认同)
  • @Jon - 我读Beau的评论是个笑话 (3认同)
  • @oxbow_lakes:可能.这很难说. (2认同)
  • @Jon Skeet有一个子字符串查找算法,它从字符串的末尾搜索到前面(http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm),其系数小于前向搜索.您可以在没有此问题的串联的情况下实现它,但我的观点是,优雅的答案与您建议的效率相同. (2认同)
  • @NickLarsen:当你发现这个伎俩时,优雅的答案肯定是要走的路.如果我不知道这个伎俩的话,我的答案就是我在实际采访中可能给出的答案.我当然不会开始实现复杂的子串算法. (2认同)

pol*_*nts 17

这是一个使用正则表达式只是为了好玩:

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

如果可以使用保证不在任何字符串中的特殊分隔符,则可以使其更简单一些.

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

您也可以使用有限重复的lookbehind:

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

  • 成为正则表达式主人的+1. (6认同)

use*_*691 10

哇,哇......为什么每个人都对O(n^2)答案如此激动?我很肯定我们可以在这里做得更好.上面的答案包括循环中的O(n)操作O(n)(substring/indexOf调用).即使使用更有效的搜索算法; 说Boyer-Moore或者KMP,最糟糕的情况仍然O(n^2)是重复.

一个O(n)随机的答案是简单的; 采用支持O(1)滑动窗口的哈希(如拉宾指纹); 哈希字符串1,然后哈希字符串2,继续在字符串周围移动哈希1的窗口,看看哈希函数是否冲突.

如果我们想象最糟糕的情况就像是"扫描两股DNA",那么碰撞的概率就会上升,这可能会退化为某种类似的O(n^(1+e))东西(只是在这里猜测).

最后,有一个确定性的O(nlogn)解决方案,外面有一个非常大的常数.基本上,这个想法是对两个字符串进行卷积.卷积的最大值将是旋转差(如果它们被旋转); 一O(n)检查确认.好的是,如果有两个相等的最大值,那么它们都是有效的解决方案.您可以使用两个FFT和一个点积以及一个iFFT进行卷积nlogn + nlogn + n + nlogn + n == O(nlogn).

由于你不能用零填充,并且你不能保证字符串长度为2 ^ n,因此FFT不会是快速的; 它们将是缓慢的,O(nlogn)但仍然是比CT算法更大的常数.

所有这一切,我绝对是100%肯定的,这里有一个确定性的O(n)解决方案,但是如果我能找到它就会变得愚蠢.


小智 8

拳头,确保2个琴弦长度相同.然后在C中,您可以使用简单的指针迭代来完成此操作.


int is_rotation(char* s1, char* s2)
{
  char *tmp1;
  char *tmp2;
  char *ref2;

  assert(s1 && s2);
  if ((s1 == s2) || (strcmp(s1, s2) == 0))
    return (1);
  if (strlen(s1) != strlen(s2))
    return (0);

  while (*s2)
    {
      tmp1 = s1;
      if ((ref2 = strchr(s2, *s1)) == NULL)
        return (0);
      tmp2 = ref2;
      while (*tmp1 && (*tmp1 == *tmp2))
        {
          ++tmp1;
          ++tmp2;
          if (*tmp2 == '\0')
            tmp2 = s2;
        }
      if (*tmp1 == '\0')
        return (1);
      else
        ++s2;
    }
  return (0);
}
Run Code Online (Sandbox Code Playgroud)

  • 啊,C.为什么在一半的时间和代码中做一些事情,你可以用C做! (19认同)
  • @Beau Martinez - 因为有时执行时间比开发时间更重要:-) (12认同)
  • +1这是非常好的C.并且公平地说,问题标记为'c'. (11认同)
  • 在这段代码中,如果不是3次(strlen和strcmp),你已经走了至少2个字符串.您可以自己保存此检查,并且可以将该逻辑保留在循环中.在循环时,如果一个字符串字符数与另一个不同,则退出循环.您将知道长度,因为您知道开始,并且您知道何时击中空终止符. (5认同)
  • @phkahler - 事情可能会慢一些.其他语言中的内置索引函数通常使用快速字符串搜索算法,如Boyer-Moore,Rabin-Karp或Knuth-Morris-Pratt.只是重新发明C中的所有东西太过天真,并认为它更快. (2认同)

Mac*_*ehl 8

这是一个O(n)和现在的alghoritm.它使用<运算符作为字符串的元素.当然不是我的.我从这里拿了它(这个网站很流行.我曾经偶然发现过一次,我现在用英语找不到那样的东西,所以我展示了我的东西:)).

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;
}
Run Code Online (Sandbox Code Playgroud)

  • +1表示时间最佳且代码大小接近最佳的解决方案(二进制和LoC).通过解释,这个答案会更好. (4认同)

Zac*_*112 7

我想这样做更好Java:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && (s1+s1).contains(s2);
}
Run Code Online (Sandbox Code Playgroud)

在Perl我会这样做:

sub isRotation {
 my($string1,$string2) = @_;
 return length($string1) == length($string2) && ($string1.$string1)=~/$string2/;
}
Run Code Online (Sandbox Code Playgroud)

甚至更好地使用索引函数而不是正则表达式:

sub isRotation {
 my($string1,$string2) = @_;
 return length($string1) == length($string2) && index($string2,$string1.$string1) != -1;
}
Run Code Online (Sandbox Code Playgroud)

  • `\ Q`引用`$ string2`中的任何特殊字符.没有它,`.`将被视为任何1个字符的字符串的旋转. (3认同)

Edm*_*und 6

不确定这是否是最有效的方法,但它可能相对有趣:Burrows-Wheeler变换.根据WP的文章,输入的所有旋转产生相同的输出.对于诸如压缩之类的应用,这是不可取的,因此指示原始旋转(例如通过索引;参见文章).但对于简单的旋转独立比较,它听起来很理想.当然,它不一定非常有效!


phk*_*ler 6

将每个字符作为幅度并对它们执行离散傅立叶变换.如果它们仅通过旋转而不同,则频谱将与舍入误差内的频率相同.当然这是低效的,除非长度是2的幂,所以你可以做FFT :-)


Mic*_*uen 5

还没有人提供模数方法,所以这里有一个:

static void Main(string[] args)
{
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "ztackoverflow"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "ackoverflowst"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "overflowstack"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "stackoverflwo"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "tackoverflwos"));
    Console.ReadLine();
}

public static bool IsRotation(string a, string b)
{
    Console.WriteLine("\nA: {0} B: {1}", a, b);

    if (b.Length != a.Length)
        return false;

    int ndx = a.IndexOf(b[0]);
    bool isRotation = true;
    Console.WriteLine("Ndx: {0}", ndx);
    if (ndx == -1) return false;
    for (int i = 0; i < b.Length; ++i)
    {
        int rotatedNdx = (i + ndx) % b.Length;
        char rotatedA = a[rotatedNdx];

        Console.WriteLine( "B: {0} A[{1}]: {2}", b[i], rotatedNdx, rotatedA );

        if (b[i] != rotatedA)
        {
            isRotation = false;
            // break; uncomment this when you remove the Console.WriteLine
        }
    }
    return isRotation;
}
Run Code Online (Sandbox Code Playgroud)

输出:

A: stackoverflow B: ztackoverflow
Ndx: -1
Rotation : False

A: stackoverflow B: ackoverflowst
Ndx: 2
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
Rotation : True

A: stackoverflow B: overflowstack
Ndx: 5
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
Rotation : True

A: stackoverflow B: stackoverflwo
Ndx: 0
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
B: o A[12]: w
Rotation : False

A: stackoverflow B: tackoverflwos
Ndx: 1
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
B: o A[12]: w
B: s A[0]: s
Rotation : False
Run Code Online (Sandbox Code Playgroud)

[编辑:2010-04-12]

piotr注意到我上面代码中的缺陷.当字符串中的第一个字符出现两次或更多时,它会出错.例如,当它应该是真的时,stackoverflow测试反对owstackoverflow导致错误.

感谢piotr发现错误.

现在,这是更正后的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace TestRotate
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ztackoverflow"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ackoverflowst"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "overflowstack"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "stackoverflwo"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "tackoverflwos"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "owstackoverfl"));

            Console.ReadLine();
        }

        public static bool IsRotation(string a, string b)
        {
            Console.WriteLine("\nA: {0} B: {1}", a, b);

            if (b.Length != a.Length)
                return false;

            if (a.IndexOf(b[0]) == -1 )
                return false;

            foreach (int ndx in IndexList(a, b[0]))
            {
                bool isRotation = true;

                Console.WriteLine("Ndx: {0}", ndx);

                for (int i = 0; i < b.Length; ++i)
                {
                    int rotatedNdx = (i + ndx) % b.Length;
                    char rotatedA = a[rotatedNdx];

                    Console.WriteLine("B: {0} A[{1}]: {2}", b[i], rotatedNdx, rotatedA);

                    if (b[i] != rotatedA)
                    {
                        isRotation = false;
                        break;
                    }
                }
                if (isRotation)
                    return true;
            }
            return false;
        }

        public static IEnumerable<int> IndexList(string src, char c)
        {
            for (int i = 0; i < src.Length; ++i)
                if (src[i] == c)
                    yield return i;
        }

    }//class Program
}//namespace TestRotate
Run Code Online (Sandbox Code Playgroud)

这是输出:

A: stackoverflow B: ztackoverflow
Rotation : False

A: stackoverflow B: ackoverflowst
Ndx: 2
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
Rotation : True

A: stackoverflow B: overflowstack
Ndx: 5
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
Rotation : True

A: stackoverflow B: stackoverflwo
Ndx: 0
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
Rotation : False

A: stackoverflow B: tackoverflwos
Ndx: 1
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
Rotation : False

A: stackoverflow B: owstackoverfl
Ndx: 5
B: o A[5]: o
B: w A[6]: v
Ndx: 11
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
Rotation : True
Run Code Online (Sandbox Code Playgroud)

这是lambda方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IsRotation
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ztackoverflow"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ackoverflowst"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "overflowstack"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "stackoverflwo"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "owstackoverfl"));

            string strToTestFrom = "stackoverflow";
            foreach(string s in StringRotations(strToTestFrom))
            {
                Console.WriteLine("is {0} rotation of {1} ? {2}",
                    s, strToTestFrom,
                    IsRotation(strToTestFrom, s) );
            }
            Console.ReadLine();
        }

        public static IEnumerable<string> StringRotations(string src)
        {
            for (int i = 0; i < src.Length; ++i)
            {
                var sb = new StringBuilder();
                for (int x = 0; x < src.Length; ++x)
                    sb.Append(src[(i + x) % src.Length]);

                yield return sb.ToString();
            }
        }

        public static bool IsRotation(string a, string b)
        {
            if (b.Length != a.Length || a.IndexOf(b[0]) < 0 ) return false;
            foreach(int ndx in IndexList(a, b[0]))
            {
                int i = ndx;
                if (b.ToCharArray().All(x => x == a[i++ % a.Length]))
                    return true;
            }
            return false;
        }

        public static IEnumerable<int> IndexList(string src, char c)
        {
            for (int i = 0; i < src.Length; ++i)
                if (src[i] == c)
                    yield return i;
        }

    }//class Program

}//namespace IsRotation
Run Code Online (Sandbox Code Playgroud)

这是lambda方法输出:

Rotation : False
Rotation : True
Rotation : True
Rotation : False
Rotation : True
is stackoverflow rotation of stackoverflow ? True
is tackoverflows rotation of stackoverflow ? True
is ackoverflowst rotation of stackoverflow ? True
is ckoverflowsta rotation of stackoverflow ? True
is koverflowstac rotation of stackoverflow ? True
is overflowstack rotation of stackoverflow ? True
is verflowstacko rotation of stackoverflow ? True
is erflowstackov rotation of stackoverflow ? True
is rflowstackove rotation of stackoverflow ? True
is flowstackover rotation of stackoverflow ? True
is lowstackoverf rotation of stackoverflow ? True
is owstackoverfl rotation of stackoverflow ? True
is wstackoverflo rotation of stackoverflow ? True
Run Code Online (Sandbox Code Playgroud)