¿Encontrar el ciclo de repetición más corto en Word?

Estoy a punto de escribir una función que me devolvería un período más corto de grupo de letras que eventualmente crearía la palabra dada.

Por ejemplo palabra abkebabkebabkeb es creado por repetido abkeb palabra. Me gustaría saber qué tan eficientemente analizar la palabra de entrada, para obtener el período más corto de caracteres que crean la palabra de entrada.

preguntado el 16 de mayo de 11 a las 17:05

@Tony The Tiger, el resultado (período más corto) no tiene que ser una palabra real. -

13 Respuestas

O (n) solución. Supone que debe cubrirse toda la cadena. La observación clave es que generamos el patrón y lo probamos, pero si encontramos algo en el camino que no coincide, debemos incluir la cadena completa que ya probamos, para no tener que volver a observar esos caracteres.

def pattern(inputv):
    pattern_end =0
    for j in range(pattern_end+1,len(inputv)):

        pattern_dex = j%(pattern_end+1)
        if(inputv[pattern_dex] != inputv[j]):

            pattern_end = j;
            continue

        if(j == len(inputv)-1):
            print pattern_end
            return inputv[0:pattern_end+1];
    return inputv;

contestado el 17 de mayo de 11 a las 06:05

Is for pattern_end in range(len(inputv)/2) ¿necesario? No creo que lo sea. - Ishtar

@Ishtar - lo siento, no te estoy siguiendo. ¿Te refieres al aspecto de la parte len () / 2? DFB

Quiero decir, reemplazando esa línea con pattern_end = 0. - Ishtar

Me temo que el algoritmo es incorrecto. Tenga en cuenta la entrada: "BCBDBCBCBDBC". El patrón de repetición más pequeño es "BCBDBC", pero el algoritmo anterior lo perderá. Además, creo que no se ocupa correctamente del caso "HELLOHELL" (donde devuelve "HELLO" en lugar de la cadena completa). - Eyal Schneider

@EyalSchneider devolver "HELLO" por "HELLOHELL" tiene mucho sentido. De lo contrario, podría estar utilizando la longitud de la cadena para determinar qué patrones están permitidos. Solo necesita verificar patrones cuyas longitudes sean combinaciones de los factores primos (y 1) de la longitud de la entrada. Por ejemplo, si obtiene una cadena de 101 caracteres de longitud, ¿cuál es el punto de intentar incluso una subsecuencia de 5 letras? No encajará perfectamente en 101 caracteres. Si la longitud de su entrada es 13, no tiene sentido verificar incluso, ningún patrón (excepto un patrón de 1 letra) repetido n veces puede sumar 13. - Boris

Aquí hay un algoritmo O (n) correcto. El primer bucle for es la parte de creación de tablas de KMP. Hay varias pruebas de que siempre se ejecuta en tiempo lineal.

Dado que esta pregunta tiene 4 respuestas anteriores, ninguna de las cuales es O (n) y correcta, probé en gran medida esta solución tanto para la corrección como para el tiempo de ejecución.

def pattern(inputv):
    if not inputv:
        return inputv

    nxt = [0]*len(inputv)
    for i in range(1, len(nxt)):
        k = nxt[i - 1]
        while True:
            if inputv[i] == inputv[k]:
                nxt[i] = k + 1
                break
            elif k == 0:
                nxt[i] = 0
                break
            else:
                k = nxt[k - 1]

    smallPieceLen = len(inputv) - nxt[-1]
    if len(inputv) % smallPieceLen != 0:
        return inputv

    return inputv[0:smallPieceLen]

Respondido 11 Feb 19, 04:02

Entonces, ¿es esta una solución que se le ocurrió o es un algoritmo conocido? - davidbuttar

Bien KMP es un algoritmo conocido. Esta pregunta era muy similar a un problema de tarea que tenía, y esta es la respuesta que se me ocurrió para la tarea. La solución del instructor fue un poco diferente, pero también usó KMP. - Buge

Hola Buge, me encanta tu solución y vota a favor. pero confundido por esta linea smallPieceLen = len(inputv) - nxt[-1], nxt[-1] significa que si toda la cadena no coincide, qué índice se utilizará para comparar a continuación. smallPieceLen representa las diferencias en la longitud total de la cuerda y nxt[-1], ¿cómo podría representarse como la cadena repetitiva más corta? - Lin Ma

@LinMa: (Buge no estuvo activo últimamente) nxt[-1] means if the whole string does not match, what index we will be used to compare next no (gramática retorcida, por cierto). Es el índice que se va a comparar a continuación cuando todo el patrón coincide y desea encontrar su siguiente aparición en un texto más largo. nxt[i] = p significa pattern[i+1-p:i+1] iguales pattern[0:p] (&! = para p+1). nxt[-1] es el índice que se comparará a continuación si el "primer" desajuste fue "en len+1 ". (En muchas presentaciones / implementaciones de KMP, hay un valor especial de -1 en el índice 0, con los valores n como arriba" desplazados a un índice más alto en uno ".) - anciano

@LinMa: (both son notificados, de todos modos) Déjame llamar len(inputv) len y nxt[-1] matchLen. Si matchLen < smallPieceLen, la única oportunidad para smallPieceLen para dividir len es ser igual a él. Si smallPieceLenmatchLen, inputv[0:smallPieceLen] iguales inputv[smallPieceLen:2*smallPieceLen], k nunca se reinició (de nuevo): inputv se compone de repeticiones de inputv[0:smallPieceLen] - la verificación de divisibilidad solo asegura que termina con una repetición completa. - anciano

Este es un ejemplo de PHP:

<?php
function getrepeatedstring($string) {
    if (strlen($string)<2) return $string;
    for($i = 1; $i<strlen($string); $i++) {
        if (substr(str_repeat(substr($string, 0, $i),strlen($string)/$i+1), 0, strlen($string))==$string)
            return substr($string, 0, $i);
    }
    return $string;
}
?>

contestado el 17 de mayo de 11 a las 06:05

Esto devuelve 'abkeb', que debería ser correcto, pero no estoy seguro de qué manera el OP solicita 'kebab' en lugar de 'abkeb'. - pimvdb

Esto es lo que busco. Pero corre en O (n). ¿Alguna idea de si esto se puede acelerar? - jack44

@ jack44: No puede saber si tiene el ciclo más corto hasta que haya examinado toda la cadena. A menos que tenga otros conocimientos, como cuál podría ser el ciclo más grande posible. Puede ser que el último carácter de la cadena deseche todo el ciclo, no lo sabes. - mellamokb

No sé PHP, pero parece que es O (n ^ 2). - DFB

@ Richard86 - Sin embargo, la comparación de cadenas va a O (n), ¿no es así? - DFB

Creo que hay una solución recursiva muy elegante. Muchas de las soluciones propuestas resuelven la complejidad adicional donde la cuerda termina con parte del patrón, como abcabca. Pero no creo que se pida.

Mi solución para la versión simple del problema en clojure:

 (defn find-shortest-repeating [pattern string]
  (if (empty? (str/replace string pattern ""))
   pattern
   (find-shortest-repeating (str pattern (nth string (count pattern))) string)))

(find-shortest-repeating "" "abcabcabc") ;; "abc"

Pero tenga en cuenta que esto no encontrará patrones incompletos al final.

Respondido 05 Abr '17, 00:04

Encontré una solución basada en su publicación, que podría tener un patrón incompleto:

(defn find-shortest-repeating [pattern string]
   (if (or (empty? (clojure.string/split string (re-pattern pattern)))
          (empty? (second (clojure.string/split string (re-pattern pattern)))))
    pattern
    (find-shortest-repeating (str pattern (nth string (count pattern))) string)))

Respondido 12 Oct 17, 17:10

@sala (defn find-pattern-string [string] (let [pattern "" working-str string] (reduce #(if (not (or (empty? (clojure.string/split string (re-pattern %1))) (empty? (second (clojure.string/split string (re-pattern %1)))))) (str %1 %2) %1) pattern working-str))) - Alfonso Fernando Alvarez

Mi solución: La idea es encontrar una subcadena desde la posición cero de modo que sea igual a la subcadena adyacente de la misma longitud, cuando se encuentre dicha subcadena, devuelva la subcadena. Tenga en cuenta que si no se encuentra una subcadena repetida, estoy imprimiendo la cadena de entrada completa.

public static void repeatingSubstring(String input){
    for(int i=0;i<input.length();i++){
        if(i==input.length()-1){
            System.out.println("There is no repetition "+input);
        }
        else if(input.length()%(i+1)==0){
            int size = i+1;
            if(input.substring(0, i+1).equals(input.substring(i+1, i+1+size))){
                System.out.println("The subString which repeats itself is "+input.substring(0, i+1));
                break;
            }
        }
    }
}

Respondido 12 Feb 18, 00:02

Creo que esto fallaría para la cadena "ababcababc" - JR

Solución Regex:

Paso 1: Separe cada carácter con un carácter delimitador que no sea parte de la cadena de entrada, incluido uno final (es decir, ~):

(.)
$1~

Entrada de ejemplo: "abkebabkebabkeb"
Ejemplo de salida: "a~b~k~e~b~a~b~k~e~b~a~b~k~e~b~"

Pruébelo en línea en Retina. (NOTA: retina es un lenguaje de programación basado en Regex diseñado para realizar pruebas rápidas de expresiones regulares y poder competir con éxito en desafíos de code-golf.)

Paso 2: Utilice la siguiente expresión regular para encontrar la subcadena repetida más corta (donde ~ es nuestro carácter delimitador elegido):

^(([^~]+~)*?)\1*$
$1

Explicación:

^(([^~]+~)*?)\1*$
^               $    # Start and end, to match the entire input-string
  ([^~]+~)           # Capture group 1: One or more non-'~' followed by a '~'
 (        *?)        # Capture group 2: Repeated zero or more time optionally
             \1*     # Followed by the first capture group repeated zero or more times

$1                   # Replace the entire input-string with the first capture group match

Entrada de ejemplo: "a~b~k~e~b~a~b~k~e~b~a~b~k~e~b~"
Ejemplo de salida: "a~b~k~e~b~"

Pruébelo en línea en Retina.

Paso 3: Elimine nuestro carácter delimitador nuevamente para obtener el resultado deseado.

~
<empty>

Entrada de ejemplo: "a~b~k~e~b~"
Ejemplo de salida: "abkeb"

Pruébelo en línea en Retina.

Aquí una implementación de ejemplo en Java.

Respondido el 20 de junio de 20 a las 12:06

Esta es una solución que se me ocurrió usando la cola, pasó todos los casos de prueba de un problema similar en las fuerzas de código. El problema no es 745A.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    string s, s1, s2; cin >> s; queue<char> qu; qu.push(s[0]); bool flag = true; int ind = -1;
    s1 = s.substr(0, s.size() / 2);
    s2 = s.substr(s.size() / 2);
    if(s1 == s2)
    {
        for(int i=0; i<s1.size(); i++)
        {
            s += s1[i];
        }
    }
    //cout << s1 << " " << s2 << " " << s << "\n";
    for(int i=1; i<s.size(); i++)
    {
        if(qu.front() == s[i]) {qu.pop();}
        qu.push(s[i]);
    }
    int cycle = qu.size();

    /*queue<char> qu2 = qu; string str = "";
    while(!qu2.empty())
    {
        cout << qu2.front() << " ";
        str += qu2.front();
        qu2.pop();
    }*/


    while(!qu.empty())
    {
        if(s[++ind] != qu.front()) {flag = false; break;}
        qu.pop();
    }
    flag == true ? cout << cycle : cout << s.size();
    return 0;
}

Respondido 02 Feb 20, 10:02

El más fácil en Python:

def pattern(self, s):
    ans=(s+s).find(s,1,-1)
    return len(pat) if ans == -1 else ans

Respondido 27 ago 20, 00:08

Será útil si explica lo que hizo: thisisjaymehta

La respuesta más simple que puedo encontrar en una entrevista es solo una solución O (n ^ 2), que prueba todas las combinaciones de subcadenas a partir de 0.

int findSmallestUnit(string str){
    for(int i=1;i<str.length();i++){
        int j=0;
        for(;j<str.length();j++){
            if(str[j%i] != str[j]){
                break;
            }
        }
        if(j==str.length()) return str.substr(0,i);
    }
    return str;
}

Ahora, si alguien está interesado en la solución O (n) a este problema en c ++:

  int findSmallestUnit(string str){
      vector<int> lps(str.length(),0);
      int i=1;
      int len=0;

      while(i<str.length()){
          if(str[i] == str[len]){
              len++;
              lps[i] = len;
              i++;
          }
          else{
              if(len == 0) i++;
              else{
                  len = lps[len-1];
              }
          }
      }
      int n=str.length();
      int x = lps[n-1];
      if(n%(n-x) == 0){
          return str.substr(0,n-x);    
      }
      return str;
  }

Lo anterior es solo la respuesta de @ Buge en c ++, ya que alguien preguntó en los comentarios.

Respondido el 31 de diciembre de 20 a las 03:12

Respuesta súper retrasada, pero recibí la pregunta en una entrevista, aquí estaba mi respuesta (probablemente no sea la más óptima, pero también funciona para casos de prueba extraños).

private void run(String[] args) throws IOException {
    File file = new File(args[0]);
    BufferedReader buffer = new BufferedReader(new FileReader(file));
    String line;
    while ((line = buffer.readLine()) != null) {
        ArrayList<String> subs = new ArrayList<>();
        String t = line.trim();
        String out = null;
        for (int i = 0; i < t.length(); i++) {
            if (t.substring(0, t.length() - (i + 1)).equals(t.substring(i + 1, t.length()))) {
                subs.add(t.substring(0, t.length() - (i + 1)));
            }
        }
        subs.add(0, t);
        for (int j = subs.size() - 2; j >= 0; j--) {
            String match = subs.get(j);
            int mLength = match.length();
            if (j != 0 && mLength <= t.length() / 2) {
                if (t.substring(mLength, mLength * 2).equals(match)) {
                    out = match;
                    break;
                }
            } else {
                out = match;
            }
        }
        System.out.println(out);
    }
}

Casos de prueba:

abcabcabcabc
bcbcbcbcbcbcbcbcbcbcbcbcbcbc
dddddddddddddddddddd
adcdefg
bcbdbcbcbdbc
Hola infierno

Devuelve el código:

abecedario
bc
d
adcdefg
bcbdbc
Hola infierno

Respondido 03 ago 15, 19:08

Solo mirando el primer ciclo for, este es O (n ^ 2), porque cada .equals () puede tomar n tiempo. - Buge

Funciona en casos como bcbdbcbcbdbc.

function smallestRepeatingString(sequence){
  var currentRepeat = '';
  var currentRepeatPos = 0;

  for(var i=0, ii=sequence.length; i<ii; i++){
    if(currentRepeat[currentRepeatPos] !== sequence[i]){
      currentRepeatPos = 0;
      // Add next character available to the repeat and reset i so we don't miss any matches inbetween
      currentRepeat = currentRepeat + sequence.slice(currentRepeat.length, currentRepeat.length+1);
      i = currentRepeat.length-1;
    }else{
      currentRepeatPos++;
    }
    if(currentRepeatPos === currentRepeat.length){
      currentRepeatPos = 0;
    }
  }

  // If repeat wasn't reset then we didn't find a full repeat at the end.
  if(currentRepeatPos !== 0){ return sequence; }

  return currentRepeat;
}

Respondido 19 ago 16, 19:08

En realidad, esto es O (n ^ 2). Eso es porque reiniciaste i ser más pequeño con i = currentRepeat.length-1;. Entonces, con una cadena de 10 caracteres ling 'aaaaaaaaab', se necesitan 46 iteraciones. Con una cadena de 20 caracteres, se necesitan 191 iteraciones. - Buge

Se me ocurrió una solución simple que funciona perfectamente incluso con cadenas muy grandes.
Implementación de PHP:

function get_srs($s){
    $hash = md5( $s );
    $i = 0; $p = '';

    do {
        $p .= $s[$i++];
        preg_match_all( "/{$p}/", $s, $m );
    } while ( ! hash_equals( $hash, md5( implode( '', $m[0] ) ) ) );

    return $p;
}

Respondido el 15 de Septiembre de 16 a las 23:09

Sería bueno que dieras algunos detalles sobre por qué funciona exactamente esto. Proporcionar más detalles ayuda a toda la comunidad y ayuda a obtener más votos. - Charlie Fish

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.