Pregunta de la entrevista: Verifique si una cadena es una rotación de otra cadena [cerrada]


235

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 s1y s2cómo comprobará si s1es 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 s2y encuentra el prefijo más largo que es una subcadena de s1, que te dará el punto de rotación. Una vez que encuentre ese punto, rompa s2en ese punto para obtener s2ay s2b, 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.


44
No tiene que verificar si concatenar (s2a, s2b) == s1, porque sabe que s2a es igual al comienzo de s1. Simplemente puede verificar si s2b == subcadena de s1 desde rotación_punto hasta el final.
Jason Hall

33
¿Cómo estas preguntas y la respuesta principal obtuvieron tantos votos positivos?
David Johnstone

99
@David: Porque es interesante.
Cam

66
Yo diría, muy interesante y una respuesta elegante y simple.
Guru

77
@David: porque es una pregunta que no se ha hecho antes aquí y también que todos entienden (si uno no entiende la pregunta / respuesta, por lo general seguramente no la votará; una pregunta bastante simple tiene una audiencia más amplia) y también porque esto se ha etiquetado con Java y C. Cuenta :)
BalusC

Respuestas:


687

En primer lugar asegúrese s1y s2son de la misma longitud. Luego verifique si s2hay una subcadena de s1concatenados con 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

En Java:

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

49
Me gusta su elegancia, pero tuve que pensar un rato para comprobar que no había falsos positivos. (No creo que hay.)
Jon Skeet

66
También puedes usarlo (s1+s1).contains(s2)en Java.
Polygenelubricants

44
De todos modos, me opondría un poco a esto como una pregunta de entrevista. Tiene un "¡ajá!" componente, creo. La mayoría de los programadores (incluido yo) simplemente usarían la fuerza bruta, que de todos modos no es irrazonable, y eso puede parecer que no es lo suficientemente "inteligente" para el entrevistador.
Daniel Daranas

55
@ Jon Concentrate en s1+s1. Claramente, todas sus subcadenas con tamaño s1.lengthson rotaciones de s1, por construcción. Por lo tanto, cualquier cadena de tamaño s1.lengthque sea una subcadena s1+s1debe ser una rotación de s1.
Daniel C. Sobral

66
@unicornaddict: lo bueno de esta solución es que es tan obvio una vez que lo señalas, ¡me odio por no pensar en ello!
James B

101

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.


14
+1 por pura mejilla. Made my day :-)
Platinum Azure

55
Si no están de acuerdo, puede vincularlos a esta pregunta.
Cam

51
Sacar su teléfono celular durante una entrevista podría considerarse grosero y al final terminarían contratando a Jon Skeet.
tstenner

2
Eso es probablemente exactamente lo que hubiera dicho
Chris Dutrow

66
No creo que puedan pagar Jon Skeet.
SoluciónYogi

49

Otro ejemplo de Python (basado en LA respuesta):

def isrotation(s1,s2):
     return len(s1)==len(s2) and s1 in 2*s2

1
Curiosamente pensé en duplicados en s2lugar de s1demasiado ... luego me di cuenta de que la relación era simétrica de todos modos.
Matthieu M.

1
Si la cadena puede ser larga, aquí hay una versión de Python que utiliza Boyer-Moore para obtener el tiempo de ejecución de O (n): def isrotation (s1, s2): return len (s1) == len (s2) y re.compile (re .escape (s1)) .búsqueda (2 * s2) no es Ninguno
Duncan

2
@Duncan: ¿el inoperador no utiliza un algoritmo O (n)?
Ken Bloom

1
@Duncan: los métodos de cadena de Python utilizan un Boyer-Moore-Horspool optimizado. Me pregunto si Java tiene optimizaciones similares.
Thomas Ahle

1
@ Thomas gracias por señalar eso. Pensé que solo las expresiones regulares usaban Boyer-Moore, pero veo que estaba equivocado. Para Python 2.4 y anteriores, mi respuesta fue correcta, pero desde Python 2.5 s1 in s2está 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.
Duncan

32

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 ):

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

ejemplo de trabajo


55
+1 para ideone.com: ¡se ve muy interesante!
Martin Vseticka

25

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.


8
+1: no necesitamos soluciones elegantes que se ejecuten en más de 3 veces la solución más eficiente. Esto es C ... la micro optimización es rigurosa .
Stephen C

8
Entrevistador: Lotta habla, pero apuesto a que este tipo no puede codificar.
Humphrey Bogart

8
@Beau: Si alguien quiere pensar eso, pueden pedirme un código. Si alguien me pregunta "cómo haría algo", generalmente describo el algoritmo en lugar de saltar al código.
Jon Skeet

3
@ Jon - Leí el comentario de Beau como una broma
oxbow_lakes

37
@ Jon ¡Fue una broma! El entrevistador no entrevista a Jon Skeet, Jon Skeet lo entrevista a él.
Humphrey Bogart

17

Aquí hay uno que usa expresiones regulares solo por diversión:

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

Puede hacerlo un poco más simple si puede usar un carácter delimitador especial garantizado para no estar en ninguna de las cadenas.

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

También puede usar mirar atrás con repetición finita en su lugar:

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

66
+1 por ser un maestro de expresiones regulares.
Chris Thornton

-1 Por poner las palabras "expresiones regulares" y "diversión" en la misma declaración, sin modificar "diversión" con "no" (solo bromeando, no
rechacé

-3 por implicar que las expresiones regulares no son divertidas.
manlycode

¿Puede algún cuerpo explicar cómo funciona esta expresión regular "(. *) (. *) = \\ 2 \\ 1"!
mawia

10

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 una O(n)operación en un O(n)bucle (la subcadena / llamada indexOf). Incluso con un algoritmo de búsqueda más eficiente; digamos Boyer-Mooreo KMP, el peor de los casos todavía es O(n^2)con duplicados.

Una O(n)respuesta aleatoria es sencilla; tome un hash (como una huella digital Rabin) que admita una O(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); Un O(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, entonces nlogn + 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.


%stringsizeSe garantiza que el KMP en la cadena concatenada consigo misma (ya sea física o virtualmente con a ) es tiempo lineal.
Kragen Javier Sitaker

+1 para Rabin-Karp. A diferencia de KMP, utiliza espacio constante y es más sencillo de implementar. (También es la primera respuesta en la que pensé, en segundos, lo que hace que sea difícil ver la respuesta "correcta", porque esta está allí y es dulce). Su idea de convolución me recuerda el algoritmo de Shor: me pregunto si hay un sublineal solución cuántica, pero eso se está volviendo tonto ahora, ¿verdad?
Darius Bacon

1
RK no proporciona una solución determinista de O (n), y KMP es O (n) en el espacio, lo que podría ser indeseable. Busque la búsqueda de subcadenas de dos vías o SMOA que son O (n) en el tiempo y O (1) en el espacio. Por cierto, glibc strstr usa Two Way, pero si realmente concatena cadenas para usarlo en lugar de usar% len, volverá a O (n) en el espacio. :-)
R .. GitHub DEJA DE AYUDAR AL HIELO

8

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.


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

19
Ah, C. ¿Por qué hacer algo en la mitad del tiempo y el código cuando puedes hacerlo en C!
Humphrey Bogart

11
+1 Está muy bien escrito C. Y para ser justos, la pregunta está etiquetada con 'c'.
Nick Moore

55
En este código, ha caminado las cadenas al menos 2 si no 3 veces (en strlen y strcmp). Puede guardar esta verificación y puede mantener esa lógica en su bucle. Mientras realiza el bucle, si el recuento de caracteres de una cadena es diferente del otro, salga del bucle. Conocerá las longitudes, ya que conoce el inicio y sabe cuándo ha llegado al terminador nulo.
Nasko

12
@Beau Martinez - porque a veces el tiempo de ejecución es más importante que el tiempo de desarrollo :-)
phkahler

2
@phkahler - La cosa es que bien podría ser más lento. Las funciones de índice incorporadas en los otros idiomas suelen utilizar un algoritmo de búsqueda rápida de cadenas como Boyer-Moore, Rabin-Karp o Knuth-Morris-Pratt. Es demasiado ingenuo simplemente reinventar todo en C y asumir que es más rápido.
Thomas Ahle

8

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 :)).

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

+1 ... O (n) es muuuucho más profundo desde un punto de vista comp-sci que cualquier otra solución que no sea O (n) :)
SyntaxT3rr0r

44
+1 para una solución que es óptima en tiempo y casi óptima en tamaño de código (tanto binario como LoC). Esta respuesta sería aún mejor con una explicación.
R .. GitHub DEJA DE AYUDAR A ICE

Completamente desconcertante. ¡Necesitamos una explicación!
j_random_hacker

7

Supongo que es mejor hacer esto en Java:

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

En Perl haría:

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

o incluso mejor usando la función de índice en lugar de la expresión regular:

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

1
Se le olvidó \Qen /\Q$string2/.
Kragen Javier Sitaker

3
\Qcita cualquier carácter especial en $string2. Sin ella, .se consideraría una rotación de cualquier cadena de 1 carácter.
jackrabbit

6

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!


Dado que la transformación Burrows-Wheeler involucra el cálculo de todas las rotaciones de la cadena, seguramente no será óptima ... :-)
R .. GitHub DEJA DE AYUDAR AL HIELO

6

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 :-)


Lo hemos usado como un ejercicio de codificación interesante, no estoy seguro de que podamos evaluarlo;).
Jayshao

FFT abusó :) +1 de mi parte
Aamir

5

Nadie ofreció un enfoque de módulo todavía, así que aquí hay uno:

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

Salida:

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

[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 contra owstackoverflowresultó en falso, cuando debería ser cierto.

Gracias piotr por detectar el error.

Ahora, aquí está el código corregido:

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

Aquí está la salida:

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

Aquí está el enfoque 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

Aquí está la salida del enfoque 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

No creo que su respuesta sea correcta ya que int ndx = a.IndexOf (b [0]); solo funcionará si no hay otros elementos con el mismo valor de b [0] en la cadena.
piotr

Gracias por notar la falla. lo corrigió ahora
Michael Buen

3

Como nadie ha dado una solución C ++. aqui esta:

bool isRotation(string s1,string s2) {

  string temp = s1;
  temp += s1;
  return (s1.length() == s2.length()) && (temp.find(s2) != string::npos);
}

Un par de puntos: estás haciendo la concatenación de cadenas relativamente costosa incluso si las longitudes no coinciden; puede pasar s2 por referencia constante.
Tony Delroy

2

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:

S1 = HELLOHELLOHELLO1HELLOHELLOHELLO2

S2 = HELLOHELLOHELLO2HELLOHELLOHELLO1

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:

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

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)


1
¿Conoces alguna plataforma que tenga strstr()en O (n + m)? Además, si el estándar (o cualquier otra cosa) no le garantiza un tiempo de ejecución lineal de strstr(), no puede afirmar que todo el algoritmo tiene una competencia de tiempo lineal.
jpalecek 01 de

Es por eso que dije que puede ser reemplazado por el algoritmo Boyer-Moore, haciendo que se ejecute en tiempo lineal.
RarrRarrRarr

Hay un par de problemas potenciales con su método de asignación 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 KMP
Kragen Javier Sitaker


2

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.


2

Una respuesta pura de Java (sin cheques nulos)

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

2

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í

  • calcular alguna suma de comprobación basada en caracteres (como xoring todos los caracteres) en ambas cadenas. Si las firmas difieren, las cadenas no son rotaciones 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.


1

Otra solución de Ruby basada en la respuesta:

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

1

Es muy fácil escribir en PHP usando strleny strposfunciones:

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

No sé qué strposusa internamente, pero si usa KMP , será lineal en el tiempo.


1

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í.


0

¿Por qué no algo como esto?


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

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:


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

Más 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: podría tener algunos problemas fuera de uno; No tengo ganas de comprobar. ;)


0

Haría esto en Perl :

sub isRotation { 
     return length $_[0] == length $_[1] and index($_[1],$_[0],$_[0]) != -1; 
}

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

0

Unirse string1con string2y utilizar el algoritmo KMP para comprobar si string2está presente en la cadena recién formado. Porque la complejidad temporal de KMP es menor que substr.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.