A un amigo mío se le hizo la siguiente pregunta hoy en una entrevista para el puesto de desarrollador de software:
Dado dos cadenas s1
y s2
cómo comprobará si s1
es una versión rotada de s2
?
Ejemplo:
Si s1 = "stackoverflow"
entonces, las siguientes son algunas de sus versiones rotadas:
"tackoverflows"
"ackoverflowst"
"overflowstack"
donde como no"stackoverflwo"
es una versión rotada.
La respuesta que dio fue:
Toma
s2
y encuentra el prefijo más largo que es una subcadena des1
, que te dará el punto de rotación. Una vez que encuentre ese punto, rompas2
en ese punto para obteners2a
ys2b
, luego, simplemente verifique siconcatenate(s2a,s2b) == s1
Parece una buena solución para mí y mi amigo. Pero el entrevistador pensó lo contrario. Pidió una solución más simple. Por favor, ayúdame diciéndome cómo harías esto Java/C/C++
.
Gracias por adelantado.
Respuestas:
En primer lugar asegúrese
s1
ys2
son de la misma longitud. Luego verifique sis2
hay una subcadena des1
concatenados cons1
:En Java:
fuente
(s1+s1).contains(s2)
en Java.s1+s1
. Claramente, todas sus subcadenas con tamaños1.length
son rotaciones des1
, por construcción. Por lo tanto, cualquier cadena de tamaños1.length
que sea una subcadenas1+s1
debe ser una rotación des1
.Seguramente una mejor respuesta sería: "Bueno, le preguntaría a la comunidad stackoverflow y probablemente tendría al menos 4 respuestas realmente buenas en 5 minutos". Los cerebros son buenos, pero le daría un valor más alto a alguien que sepa cómo trabajar con otros para obtener una solución.
fuente
Otro ejemplo de Python (basado en LA respuesta):
fuente
s2
lugar des1
demasiado ... luego me di cuenta de que la relación era simétrica de todos modos.in
operador no utiliza un algoritmo O (n)?s1 in s2
está optimizado. Ver effbot.org/zone/stringlib.htm para la descripción del algoritmo. Google parece indicar que Java no tiene una búsqueda rápida de cadenas (ver johannburkard.de/software/stringsearch por ejemplo) aunque dudo que se rompa algo si lo cambian.Como otros han presentado una solución de complejidad de tiempo cuadrática en el peor de los casos, agregaría una solución lineal (basada en el algoritmo KMP ):
ejemplo de trabajo
fuente
EDITAR: la respuesta aceptada es claramente más elegante y eficiente que esta, si la ve. Dejé esta respuesta como lo que haría si no hubiera pensado duplicar la cadena original.
Solo lo forzaría por fuerza bruta. Primero verifique la longitud y luego intente cada posible desplazamiento de rotación. Si ninguno de ellos funciona, devuelve falso; si alguno de ellos funciona, devuelve verdadero inmediatamente.
No hay necesidad particular de concatenar: solo use punteros (C) o índices (Java) y camine ambos, uno en cada cadena, comenzando por el comienzo de una cadena y el desplazamiento de rotación del candidato actual en la segunda cadena, y envolviendo cuando sea necesario . Verifique la igualdad de caracteres en cada punto de la cadena. Si llega al final de la primera cadena, ya está.
Probablemente sería tan fácil concatenar, aunque probablemente menos eficiente, al menos en Java.
fuente
Aquí hay uno que usa expresiones regulares solo por diversión:
Puede hacerlo un poco más simple si puede usar un carácter delimitador especial garantizado para no estar en ninguna de las cadenas.
También puede usar mirar atrás con repetición finita en su lugar:
fuente
Whoa, whoa ... ¿por qué todos están tan emocionados con una
O(n^2)
respuesta? Estoy seguro de que podemos hacerlo mejor aquí. LA respuesta anterior incluye unaO(n)
operación en unO(n)
bucle (la subcadena / llamada indexOf). Incluso con un algoritmo de búsqueda más eficiente; digamosBoyer-Moore
oKMP
, el peor de los casos todavía esO(n^2)
con duplicados.Una
O(n)
respuesta aleatoria es sencilla; tome un hash (como una huella digital Rabin) que admita unaO(1)
ventana deslizante; hash string 1, luego hash string 2, y proceda a mover la ventana del hash 1 alrededor de la cadena y vea si las funciones hash chocan.Si imaginamos que el peor de los casos es algo así como "escanear dos cadenas de ADN", entonces la probabilidad de colisiones aumenta, y esto probablemente degenera en algo así
O(n^(1+e))
o algo (solo adivinando aquí).Finalmente, hay una
O(nlogn)
solución determinista que tiene una constante muy grande afuera. Básicamente, la idea es tomar una convolución de las dos cadenas. El valor máximo de la convolución será la diferencia de rotación (si se rotan); UnO(n)
cheque confirma. Lo bueno es que si hay dos valores máximos iguales, entonces ambos también son soluciones válidas. Puede hacer la convolución con dos FFT y un producto de punto, y un iFFT, entoncesnlogn + nlogn + n + nlogn + n == O(nlogn)
.Como no puede rellenar con ceros y no puede garantizar que las cadenas tengan una longitud de 2 ^ n, las FFT no serán las rápidas; serán los lentos, aún así,
O(nlogn)
pero una constante mucho más grande que el algoritmo CT.Dicho todo esto, estoy absolutamente seguro de que hay una
O(n)
solución determinista aquí, pero maldita sea si puedo encontrarla.fuente
%stringsize
Se garantiza que el KMP en la cadena concatenada consigo misma (ya sea física o virtualmente con a ) es tiempo lineal.Puño, asegúrese de que las 2 cuerdas tengan la misma longitud. Luego, en C, puede hacer esto con una simple iteración de puntero.
fuente
Aquí hay un
O(n)
algoritmo en su lugar. Utiliza el<
operador para los elementos de las cadenas. No es mío, por supuesto. Lo tomé de aquí (el sitio está en polaco. Me topé con él una vez en el pasado y no pude encontrar algo así ahora en inglés, así que muestro lo que tengo :)).fuente
Supongo que es mejor hacer esto en
Java
:En Perl haría:
o incluso mejor usando la función de índice en lugar de la expresión regular:
fuente
\Q
en/\Q$string2/
.\Q
cita cualquier carácter especial en$string2
. Sin ella,.
se consideraría una rotación de cualquier cadena de 1 carácter.No estoy seguro de si este es el método más eficiente, pero podría ser relativamente interesante : la transformación Burrows-Wheeler . Según el artículo de WP, todas las rotaciones de la entrada producen la misma salida. Para aplicaciones como la compresión, esto no es deseable, por lo que se indica la rotación original (por ejemplo, mediante un índice; consulte el artículo). Pero para una simple comparación independiente de la rotación, suena ideal. ¡Por supuesto, no es necesariamente idealmente eficiente!
fuente
Tome cada personaje como una amplitud y realice una transformada discreta de Fourier en ellos. Si difieren solo por rotación, los espectros de frecuencia serán los mismos dentro del error de redondeo. Por supuesto, esto es ineficiente a menos que la longitud sea una potencia de 2 para que pueda hacer una FFT :-)
fuente
Nadie ofreció un enfoque de módulo todavía, así que aquí hay uno:
Salida:
[EDITAR: 2010-04-12]
Piotr notó la falla en mi código de arriba. Se produce un error cuando el primer carácter de la cadena aparece dos veces o más. Por ejemplo,
stackoverflow
probado contraowstackoverflow
resultó en falso, cuando debería ser cierto.Gracias piotr por detectar el error.
Ahora, aquí está el código corregido:
Aquí está la salida:
Aquí está el enfoque lambda:
Aquí está la salida del enfoque lambda:
fuente
Como nadie ha dado una solución C ++. aqui esta:
fuente
El simple truco de rotación del puntero de Opera funciona, pero es extremadamente ineficiente en el peor de los casos en tiempo de ejecución. Simplemente imagine una cadena con muchas largas series repetitivas de caracteres, es decir:
El "ciclo hasta que haya una falta de coincidencia, luego incremente en uno e intente nuevamente" es un enfoque horrible, computacionalmente.
Para probar que puede hacer el enfoque de concatenación en C simple sin demasiado esfuerzo, aquí está mi solución:
Esto es lineal en tiempo de ejecución, a expensas del uso de memoria O (n) en gastos generales.
(Tenga en cuenta que la implementación de strstr () es específica de la plataforma, pero si es particularmente mortal, siempre se puede reemplazar con una alternativa más rápida, como el algoritmo Boyer-Moore)
fuente
strstr()
en O (n + m)? Además, si el estándar (o cualquier otra cosa) no le garantiza un tiempo de ejecución lineal destrstr()
, no puede afirmar que todo el algoritmo tiene una competencia de tiempo lineal.s1SelfConcat
: es solo desde C9x que C permite tamaños de matriz variables (aunque GCC lo ha permitido por mucho más tiempo), y tendrá problemas para asignar cadenas grandes en la pila. Yosef Kreinin escribió una publicación de blog muy divertida sobre este problema. Además, su solución sigue siendo tiempo cuadrático con Boyer-Moore; quieres KMPC#:
fuente
Me gusta LA respuesta que comprueba si s2 es una subcadena de s1 concatenada con s1.
Quería agregar una optimización que no pierda su elegancia.
En lugar de concatenar las cadenas, puede usar una vista de unión (no sé para otro lenguaje, pero para C ++ Boost.Range proporciona ese tipo de vistas).
Como la comprobación de si una cadena es una subcadena de otra tiene una complejidad lineal promedio (la peor de las situaciones es cuadrática), esta optimización debería mejorar la velocidad en un factor de 2 en promedio.
fuente
Una respuesta pura de Java (sin cheques nulos)
fuente
Y ahora para algo completamente diferente.
Si desea una respuesta realmente rápida en un contexto restringido cuando las cadenas no son rotación entre sí
De acuerdo, puede fallar, pero es muy rápido decir si las cadenas no coinciden y si coinciden, aún puede usar otro algoritmo como la concatenación de cadenas para verificar.
fuente
Otra solución de Ruby basada en la respuesta:
fuente
Es muy fácil escribir en PHP usando
strlen
ystrpos
funciones:No sé qué
strpos
usa internamente, pero si usa KMP , será lineal en el tiempo.fuente
Invierta una de las cuerdas. Tome la FFT de ambos (tratándolos como simples secuencias de enteros). Multiplique los resultados en forma puntual. Transformar de nuevo usando FFT inversa. El resultado tendrá un solo pico si las cuerdas son rotaciones entre sí: la posición del pico indicará cuánto se rotan entre sí.
fuente
¿Por qué no algo como esto?
Por supuesto, podría escribir su propia función IndexOf (); No estoy seguro si .NET usa una manera ingenua o más rápida.
Ingenuo:
Más rápido:
Editar: podría tener algunos problemas fuera de uno; No tengo ganas de comprobar. ;)
fuente
Haría esto en Perl :
fuente
fuente
Unirse
string1
constring2
y utilizar el algoritmo KMP para comprobar sistring2
está presente en la cadena recién formado. Porque la complejidad temporal de KMP es menor quesubstr
.fuente