Pergunta da entrevista: Verifique se uma string é uma rotação de outra string [fechada]

235

Hoje, um amigo meu fez a seguinte pergunta na entrevista para a posição de desenvolvedor de software:

Dadas duas seqüências s1e s2como você verificará se s1é uma versão rotacionada de s2?

Exemplo:

Se s1 = "stackoverflow", a seguir, estão algumas de suas versões rotacionadas:

"tackoverflows"
"ackoverflowst"
"overflowstack"

onde como não"stackoverflwo" é uma versão rotacionada.

A resposta que ele deu foi:

Pegue s2e encontre o prefixo mais longo que é uma sub-string de s1que fornecerá o ponto de rotação. Depois de encontrar esse ponto, pare s2nesse ponto para obter s2ae s2b, em seguida, verifique seconcatenate(s2a,s2b) == s1

Parece uma boa solução para mim e meu amigo. Mas o entrevistador pensou o contrário. Ele pediu uma solução mais simples. Por favor, ajude-me dizendo como você faria isso Java/C/C++?

Desde já, obrigado.

Webdev
fonte
4
Você não precisa verificar se concatenar (s2a, s2b) == s1, porque você sabe que s2a é igual ao início de s1. Você pode apenas verificar se s2b == substring de s1 do rotation_point até o final.
Jason Hall
33
Como essas perguntas e a resposta principal obtiveram tantos votos positivos?
David Johnstone
9
@ David: Porque é interessante.
Cam
6
Eu diria, muito interessante e uma resposta simples e elegante.
Guru
7
@ David: porque é uma pergunta que não foi feita antes aqui e também que todos entendem (se alguém não entender a pergunta / resposta, normalmente não a aprovará; uma pergunta bastante simples tem um público mais amplo) e também porque este está a ser marcados com ambas as contagens Java e C. :)
BalusC

Respostas:

687

Primeiro, certifique-se s1e s2tem o mesmo comprimento. Em seguida, verifique se s2há uma subcadeia s1concatenada com 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

Em Java:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && ((s1+s1).indexOf(s2) != -1);
}
codaddict
fonte
49
Gosto da elegância, mas tive que pensar um pouco para verificar se não havia nenhum falso positivo. (Eu não acho que existem.)
Jon Skeet
6
Você também pode usar (s1+s1).contains(s2)em Java.
polygenelubricants 31/03
4
Enfim, eu me oponho um pouco a isso como uma pergunta de entrevista. Tem um "aha!" componente, eu acho. A maioria dos programadores (inclusive eu) usaria força bruta, o que não é irracional de qualquer maneira, e pode parecer não ser "inteligente" o suficiente para o entrevistador.
Daniel Daranas 31/03/10
5
@ Jon Concentre-se em s1+s1. Claramente, todos os seus substrings com tamanho s1.lengthsão rotações s1, por construção. Portanto, qualquer sequência de tamanho s1.lengthque seja uma substring s1+s1deve ser uma rotação de s1.
21910 Daniel C. Sobral
6
@unicornaddict - o que é ótimo nesta solução é que é tão óbvio quando você aponta, eu me odeio por não pensar nisso!
James B
101

Certamente, uma resposta melhor seria: "Bem, eu perguntaria à comunidade stackoverflow e provavelmente teria pelo menos 4 respostas realmente boas em 5 minutos". O cérebro é bom e tudo, mas eu daria um valor mais alto a alguém que sabe trabalhar com outras pessoas para obter uma solução.

Chris Knight
fonte
14
+1 para bochecha pura. Feito o meu dia :-)
Platinum Azure
5
Se eles discordarem, você poderá vinculá-los a esta pergunta.
Cam
51
Bater o celular durante uma entrevista pode ser considerado rude e, no final, eles acabam contratando Jon Skeet.
tstenner
2
Isso é, na verdade, provavelmente exatamente o que eu teria dito
Chris Dutrow
6
Eu não acho que eles serão capazes de pagar Jon Skeet.
SolutionYogi
49

Outro exemplo de python (com base na resposta):

def isrotation(s1,s2):
     return len(s1)==len(s2) and s1 in 2*s2
Federico A. Ramponi
fonte
1
Curiosamente, pensei em duplicar, em s2vez de s1também ... então percebi que a relação era simétrica de qualquer maneira.
Matthieu M.
1
Se a string puder ser longa, aqui está uma versão em Python que usa Boyer-Moore para obter O (n) tempo de execução: def isrotation (s1, s2): return len (s1) == len (s2) e re.compile (re . .escape (S1)) busque (2 * s2) não é None
Duncan
2
@ Duncan: o inoperador não usa um algoritmo O (n)?
Ken Bloom
1
@ Duncan: Os métodos de string python usam um Boyer-Moore-Horspool otimizado. Gostaria de saber se o Java tem otimizações semelhantes.
Thomas Ahle
1
@ Thomas obrigado por apontar isso. Eu pensava que apenas expressões regulares usavam Boyer-Moore, mas vejo que estava errado. Para o Python 2.4 e versões anteriores, minha resposta estava correta, mas como o Python 2.5 s1 in s2está otimizado. Veja effbot.org/zone/stringlib.htm para a descrição do algoritmo. O Google parece indicar que o Java não possui uma pesquisa rápida de strings (consulte johannburkard.de/software/stringsearch por exemplo), embora eu duvide que ele quebraria alguma coisa se o mudassem.
Duncan
32

Como outros usuários enviaram uma solução quadrática de complexidade de pior caso, eu adicionaria uma linear (com base no algoritmo 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;
}

exemplo de trabalho

jpalecek
fonte
5
+1 para ideone.com - parece muito interessante!
Martin Vseticka
25

EDIT: A resposta aceita é claramente mais elegante e eficiente do que isso, se você a encontrar. Deixei essa resposta como faria se não tivesse pensado em dobrar a corda original.


Eu apenas a forcei. Verifique primeiro o comprimento e tente todos os desvios de rotação possíveis. Se nenhum deles funcionar, retorne false - se algum funcionar, retorne true imediatamente.

Não há necessidade específica de concatenar - basta usar ponteiros (C) ou índices (Java) e caminhar juntos, um em cada sequência - começando no início de uma sequência e a rotação atual do candidato deslocada na segunda sequência e quebrando sempre que necessário . Verifique a igualdade de caracteres em cada ponto da sequência. Se você chegar ao final da primeira string, estará pronto.

Provavelmente seria tão fácil concatenar - embora provavelmente menos eficiente, pelo menos em Java.

Jon Skeet
fonte
8
+1 - não precisamos de soluções elegantes que sejam executadas em mais de 3 vezes a solução mais eficiente. Isso é C ... a micro-otimização é a melhor .
Stephen C
8
Entrevistador: Muita conversa, mas aposto que esse cara não pode codificar.
Humphrey Bogart
8
@Beau: Se alguém quiser pensar nisso, pode me pedir um código. Se alguém me perguntar "como eu faria alguma coisa", geralmente descrevo o algoritmo, em vez de pular para o código.
Jon Skeet
3
@Jon - Eu li o comentário de Beau como uma piada.
oxbow_lakes 31/03
37
@ Jon Foi uma piada! O entrevistador não entrevista Jon Skeet, Jon Skeet o entrevista.
Humphrey Bogart
17

Aqui está um usando regex apenas por diversão:

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

Você pode simplificar um pouco se puder usar um caractere delimitador especial garantido para não estar em nenhuma das cadeias.

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

Você também pode usar lookbehind com repetição finita:

boolean isRotation(String s1, String s2) {
   return (s1 + s2).matches(
      String.format("(.*)(.*)(?<=^.{%d})\\2\\1", s1.length())
   );
}
poligenelubricants
fonte
6
+1 por ser um mestre de regex.
Chris Thornton
-1 Para colocar as palavras "regex" e "diversão" na mesma declaração, sem modificar "diversão" com "não" (apenas brincando, eu não fiz para baixo votação)
Binary Worrier
-3 por sugerir que as expressões regulares não são divertidas.
manlycode
qualquer corpo pode explicar como esse regex "(. *) (. *) = \\ 2 \\ 1" funcionou!
Mawia
10

Whoa, whoa ... por que todo mundo está tão emocionado com uma O(n^2)resposta? Estou certo de que podemos fazer melhor aqui. A resposta acima inclui uma O(n)operação em um O(n)loop (a chamada substring / indexOf). Mesmo com um algoritmo de pesquisa mais eficiente; digamos Boyer-Mooreou KMP, o pior caso ainda éO(n^2) com duplicatas.

Uma O(n)resposta aleatória é direta; use um hash (como uma impressão digital de Rabin) que suporte umO(1) janela deslizante; hash string 1, hash string 2 e continue movendo a janela do hash 1 ao redor da string e veja se as funções hash colidem.

Se imaginarmos que o pior caso é algo como "escanear duas fileiras de DNA", a probabilidade de colisões aumenta, e isso provavelmente degenera para algo como O(n^(1+e)) ou algo (apenas adivinhe aqui).

Finalmente, há uma O(nlogn)solução determinística que tem uma constante muito grande fora. Basicamente, a idéia é levar uma convolução das duas cordas. O valor máximo da convolução será a diferença de rotação (se forem giradas); uma O(n)verificação confirma. O bom é que, se houver dois valores máximos iguais, os dois também serão soluções válidas. Você pode fazer a convolução com dois FFTs e um produto de ponto, e um iFFT, portanto nlogn + nlogn + n + nlogn + n == O(nlogn).

Como você não pode preencher com zeros e não pode garantir que as seqüências tenham 2 ^ n de comprimento, as FFTs não serão as mais rápidas; eles serão os lentos, aindaO(nlogn) uma constante muito maior que o algoritmo CT.

Tudo isso dito, estou absolutamente, 100% positivo de que existe uma O(n)solução determinística aqui, mas que se consiga encontrá-la.

user295691
fonte
O KMP na sequência concatenada consigo mesmo (fisicamente ou virtualmente com a %stringsize) é garantido como um tempo linear.
Kragen Javier Sitaker
+1 para Rabin-Karp. Ao contrário do KMP, ele usa espaço constante e é mais simples de implementar. (Também é a primeira resposta em que pensei, em segundos, dificultando a resposta 'certa', porque esta é bem ali e é legal.) solução quântica - mas isso está ficando bobo agora, certo?
Darius Bacon
1
RK não fornece uma solução determinística de O (n) e KMP é O (n) no espaço que pode ser indesejável. Procure a pesquisa de substring bidirecional ou SMOA, que é O (n) no tempo e O (1) no espaço. A propósito, o glibc strstr usa o Two Way, mas se você realmente concatena as strings para usá-lo em vez de usar% len, está de volta ao O (n) no espaço. :-)
R .. GitHub PARE DE AJUDAR ICE
8

Punho, verifique se as duas cordas têm o mesmo comprimento. Em C, você pode fazer isso com uma iteração simples do ponteiro.


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);
}
Ópera
fonte
19
Ah, C. Por que fazer algo na metade do tempo e codificar quando você pode fazê-lo em C!
Humphrey Bogart
11
+1 Está muito bem escrito C. E, para ser justo, a pergunta está marcada com 'c'.
Nick Moore
5
Neste código, você percorreu as cordas pelo menos 2, se não 3 vezes (em strlen e strcmp). Você pode salvar essa verificação e manter essa lógica em seu loop. Enquanto você faz um loop, se uma contagem de caracteres de cadeia for diferente da outra, saia do loop. Você saberá os comprimentos, como você sabe o início e quando atingir o terminador nulo.
Nasko
12
@Beau Martinez - porque o tempo de execução, por vezes, é mais importante do que o tempo de desenvolvimento :-)
phkahler
2
@phkahler - O problema é que pode muito bem ser mais lento. As funções de índice incorporadas nos outros idiomas geralmente usam um algoritmo de pesquisa rápida de cadeias, como Boyer-Moore, Rabin-Karp ou Knuth-Morris-Pratt. É ingênuo demais apenas reinventar tudo em C e assumir que seja mais rápido.
Thomas Ahle
8

Aqui está um O(n)algoritmo existente. Ele usa <operador para os elementos das strings. Não é meu, é claro. Peguei a partir daqui (o site está em polonês. Encontrei uma vez no passado e não consegui encontrar algo assim agora em inglês, então mostro o que tenho :)).

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;
}
Maciej Hehl
fonte
1 ... S (n) é apenas sooooo muito mais profunda a partir de uma amostra-sci ponto de vista do que qualquer não o (n) solução :)
SyntaxT3rr0r
4
+1 para uma solução ideal no tempo e quase ideal no tamanho do código (binário e LoC). Esta resposta seria ainda melhor com uma explicação.
R .. GitHub Pare de ajudar o gelo
Totalmente desconcertante. Precisamos de uma explicação!
Jrandom_hacker 2/10/11
7

Eu acho que é melhor fazer isso em Java:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && (s1+s1).contains(s2);
}

No Perl eu faria:

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

ou melhor ainda, usando a função index em vez da expressão regular:

sub isRotation {
 my($string1,$string2) = @_;
 return length($string1) == length($string2) && index($string2,$string1.$string1) != -1;
}
Zacky112
fonte
1
Você esqueceu \Qno /\Q$string2/.
Kragen Javier Sitaker
3
\Qcita qualquer caractere especial em $string2. Sem ele, .seria considerada uma rotação de qualquer sequência de 1 caractere.
precisa
6

Não tenho certeza se esse é o método mais eficiente, mas pode ser relativamente interessante : a transformação Burrows-Wheeler . De acordo com o artigo WP, todas as rotações da entrada produzem a mesma saída. Para aplicações como compactação, isso não é desejável, portanto a rotação original é indicada (por exemplo, por um índice; consulte o artigo). Mas, para uma comparação simples independente da rotação, parece ideal. Obviamente, não é necessariamente idealmente eficiente!

Edmund
fonte
Desde o Burrows-Wheeler transformar envolve computação todas as rotações da corda, ele certamente não vai ser o ideal .. :-)
R .. GitHub parar de ajudar ICE
6

Tome cada personagem como uma amplitude e realize uma transformação discreta de Fourier neles. Se diferirem apenas por rotação, os espectros de frequência serão os mesmos do erro de arredondamento. Claro que isso é ineficiente, a menos que o comprimento seja uma potência de 2, para que você possa executar uma FFT :-)

phkahler
fonte
Usamos isso como um exercício de codificação interessante, não tenho certeza de que poderíamos avaliar isso;).
jayshao
FFT abusou :) +1 de mim
Aamir
5

Ninguém ofereceu uma abordagem de módulo ainda, então aqui está uma:

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;
}

Resultado:

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

[EDIT: 12-04-2010]

O piotr notou a falha no meu código acima. Ele erros quando o primeiro caractere na seqüência ocorre duas ou mais vezes. Por exemplo, stackoverflowtestado contraowstackoverflow em resultado de falso, quando deveria ser verdade.

Obrigado piotr por detectar o erro.

Agora, aqui está o código corrigido:

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

Aqui está a saída:

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

Aqui está a abordagem 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

Aqui está a saída da abordagem 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
Michael Buen
fonte
Não acho que sua resposta esteja correta, pois int ndx = a.IndexOf (b [0]); funcionará apenas se não houver outros elementos com o mesmo valor de b [0] na string.
quer
obrigado por perceber a falha. corrigido agora
Michael Buen
3

Como ninguém deu uma solução em C ++. aqui está:

bool isRotation(string s1,string s2) {

  string temp = s1;
  temp += s1;
  return (s1.length() == s2.length()) && (temp.find(s2) != string::npos);
}
user324138
fonte
Alguns pontos: você está fazendo a concatenação de cordas relativamente cara, mesmo que os comprimentos não correspondam; você pode passar s2 por referência const.
Tony Delroy
2

O truque simples de rotação do ponteiro do Opera funciona, mas é extremamente ineficiente na pior das hipóteses em tempo de execução. Simplesmente imagine uma string com muitas execuções repetidas longas de caracteres, ou seja:

S1 = HELLOHELLOHELLO1HELLOHELLOHELLO2

S2 = HELLOHELLOHELLO2HELLOHELLOHELLO1

O "loop até que haja uma incompatibilidade, depois incremente um e tente novamente" é uma abordagem horrível, computacionalmente.

Para provar que você pode executar a abordagem de concatenação em C simples sem muito esforço, eis a minha solução:

  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);
}

Isso é linear no tempo de execução, às custas do uso de memória O (n) em sobrecarga.

(Observe que a implementação de strstr () é específica da plataforma, mas se é particularmente fatal, sempre pode ser substituída por uma alternativa mais rápida, como o algoritmo de Boyer-Moore)

RarrRarrRarr
fonte
1
Você conhece alguma plataforma que possua strstr()O (n + m)? Além disso, se o padrão (ou qualquer outra coisa) não garantir um tempo de execução linear de strstr(), você não poderá afirmar que todo o algoritmo possui compexidade de tempo linear.
precisa saber é o seguinte
É por isso que eu disse que ele pode ser substituído pelo algoritmo de Boyer-Moore, tornando-o executado em tempo linear.
RarrRarrRarr
Existem alguns problemas em potencial no seu método de alocação s1SelfConcat: é apenas desde C9x que C permite tamanhos de matriz variáveis ​​(embora o GCC tenha permitido isso por muito mais tempo) e você terá problemas para alocar grandes seqüências de caracteres na pilha. Yosef Kreinin escreveu uma postagem de blog muito divertida sobre esse problema. Além disso, sua solução ainda é de tempo quadrático com a Boyer-Moore; você quer KMP.
Kragen Javier Sitaker
2

C #:

s1 == null && s2 == null || s1.Length == s2.Length && (s1 + s1).Contains(s2)
Anthony Faull
fonte
2

Eu gosto da resposta que verifica se s2 é uma subcadeia de s1 concatenada com s1.

Eu queria adicionar uma otimização que não perdesse sua elegância.

Em vez de concatenar as strings, você pode usar uma visualização de junção (não sei para outro idioma, mas o C ++ Boost.Range fornece esse tipo de visualização).

Como a verificação de uma cadeia de caracteres é uma substring de outra possui complexidade média linear (a pior complexidade é quadrática), essa otimização deve melhorar a velocidade em um fator de 2 em média.

Vicente Botet Escriba
fonte
2

Uma resposta Java pura (sem verificações nulas)

private boolean isRotation(String s1,String s2){
    if(s1.length() != s2.length()) return false;
    for(int i=0; i < s1.length()-1; i++){
        s1 = new StringBuilder(s1.substring(1)).append(s1.charAt(0)).toString();
        //--or-- s1 = s1.substring(1) + s1.charAt(0)
        if(s1.equals(s2)) return true;
    }
    return false;
}
portador de anel
fonte
2

E agora para algo completamente diferente.

Se você deseja uma resposta realmente rápida em algum contexto restrito, quando as strings não são rotativas uma da outra

  • calcule alguma soma de verificação baseada em caracteres (como xoring todos os caracteres) em ambas as strings. Se as assinaturas diferem, as seqüências de caracteres não são rotações uma da outra.

Concordado, ele pode falhar, mas é muito rápido dizer se as strings não correspondem e se elas correspondem, você ainda pode usar outro algoritmo, como concatenação de strings, para verificar.

kriss
fonte
1

Outra solução rubi baseado na resposta:

def rotation?(a, b); a.size == b.size and (b*2)[a]; end
Anurag
fonte
1

É muito fácil escrever em PHP usando strlene strposfunções:

function isRotation($string1, $string2) {
    return strlen($string1) == strlen($string2) && (($string1.$string1).strpos($string2) != -1);
}

Não sei o que strposusa internamente, mas se ele usa o KMP, isso será linear no tempo.

user325894
fonte
1

Inverta uma das cordas. Tome a FFT de ambas (tratando-as como sequências simples de números inteiros). Multiplique os resultados juntos por pontos. Transforme de volta usando FFT inverso. O resultado terá um único pico se as seqüências de caracteres forem rotações uma da outra - a posição do pico indicará o quanto elas são rotacionadas uma em relação à outra.

brewbuck
fonte
0

Por que não algo assim?


//is q a rotation of p?
bool isRotation(string p, string q) {
    string table = q + q;    
    return table.IndexOf(p) != -1;
}

Obviamente, você poderia escrever sua própria função IndexOf (); Não tenho certeza se o .NET usa uma maneira ingênua ou mais rápida.

Ingênuo:


int IndexOf(string s) {
    for (int i = 0; i < this.Length - s.Length; i++)
        if (this.Substring(i, s.Length) == s) return i;
    return -1;
}

Mais rápido:


int IndexOf(string s) {
    int count = 0;
    for (int i = 0; i < this.Length; i++) {
        if (this[i] == s[count])
            count++;
        else
            count = 0;
        if (count == s.Length)
            return i - s.Length;
    }
    return -1;
}

Editar: Eu posso ter alguns problemas isolados; não sinto vontade de checar. ;)

hehewaffles
fonte
0

Eu faria isso em Perl :

sub isRotation { 
     return length $_[0] == length $_[1] and index($_[1],$_[0],$_[0]) != -1; 
}
4 rotações
fonte
0
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;
}
gerenciados
fonte
0

Associe string1-se string2e use o algoritmo KMP para verificar se string2está presente na string recém-formada. Como a complexidade temporal do KMP é menor que substr.

codaddict
fonte