Hoje, um amigo meu fez a seguinte pergunta na entrevista para a posição de desenvolvedor de software:
Dadas duas seqüências s1
e s2
como 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
s2
e encontre o prefixo mais longo que é uma sub-string des1
que fornecerá o ponto de rotação. Depois de encontrar esse ponto, pares2
nesse ponto para obters2a
es2b
, 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.
Respostas:
Primeiro, certifique-se
s1
es2
tem o mesmo comprimento. Em seguida, verifique ses2
há uma subcadeias1
concatenada coms1
:Em Java:
fonte
(s1+s1).contains(s2)
em Java.s1+s1
. Claramente, todos os seus substrings com tamanhos1.length
são rotaçõess1
, por construção. Portanto, qualquer sequência de tamanhos1.length
que seja uma substrings1+s1
deve ser uma rotação des1
.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.
fonte
Outro exemplo de python (com base na resposta):
fonte
s2
vez des1
também ... então percebi que a relação era simétrica de qualquer maneira.in
operador não usa um algoritmo O (n)?s1 in s2
está 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.Como outros usuários enviaram uma solução quadrática de complexidade de pior caso, eu adicionaria uma linear (com base no algoritmo KMP ):
exemplo de trabalho
fonte
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.
fonte
Aqui está um usando regex apenas por diversão:
Você pode simplificar um pouco se puder usar um caractere delimitador especial garantido para não estar em nenhuma das cadeias.
Você também pode usar lookbehind com repetição finita:
fonte
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 umaO(n)
operação em umO(n)
loop (a chamada substring / indexOf). Mesmo com um algoritmo de pesquisa mais eficiente; digamosBoyer-Moore
ouKMP
, 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); umaO(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, portantonlogn + 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, ainda
O(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.fonte
%stringsize
) é garantido como um tempo linear.Punho, verifique se as duas cordas têm o mesmo comprimento. Em C, você pode fazer isso com uma iteração simples do ponteiro.
fonte
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 :)).fonte
Eu acho que é melhor fazer isso em
Java
:No Perl eu faria:
ou melhor ainda, usando a função index em vez da expressão regular:
fonte
\Q
no/\Q$string2/
.\Q
cita qualquer caractere especial em$string2
. Sem ele,.
seria considerada uma rotação de qualquer sequência de 1 caractere.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!
fonte
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 :-)
fonte
Ninguém ofereceu uma abordagem de módulo ainda, então aqui está uma:
Resultado:
[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,
stackoverflow
testado contraowstackoverflow
em resultado de falso, quando deveria ser verdade.Obrigado piotr por detectar o erro.
Agora, aqui está o código corrigido:
Aqui está a saída:
Aqui está a abordagem lambda:
Aqui está a saída da abordagem lambda:
fonte
Como ninguém deu uma solução em C ++. aqui está:
fonte
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:
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:
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)
fonte
strstr()
O (n + m)? Além disso, se o padrão (ou qualquer outra coisa) não garantir um tempo de execução linear destrstr()
, você não poderá afirmar que todo o algoritmo possui compexidade de tempo linear.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.C #:
fonte
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.
fonte
Uma resposta Java pura (sem verificações nulas)
fonte
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
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.
fonte
Outra solução rubi baseado na resposta:
fonte
É muito fácil escrever em PHP usando
strlen
estrpos
funções:Não sei o que
strpos
usa internamente, mas se ele usa o KMP, isso será linear no tempo.fonte
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.
fonte
Por que não algo assim?
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:
Mais rápido:
Editar: Eu posso ter alguns problemas isolados; não sinto vontade de checar. ;)
fonte
Eu faria isso em Perl :
fonte
fonte
Associe
string1
-sestring2
e use o algoritmo KMP para verificar sestring2
está presente na string recém-formada. Como a complexidade temporal do KMP é menor quesubstr
.fonte