Aprendizaje de cadenas (algoritmo KMP)

Para preguntas sobre cadenas, solo sé cómo usar plantillas y no sé cómo cambiar el tablero sin las preguntas de plantilla.
Debo no haber entendido el significado del algoritmo en sí, por lo que he estudiado cada algoritmo en sí y algunas de sus extensiones más comunes en detalle en los últimos días. Espero ampliar mi pensamiento para resolver problemas en el futuro.

Explicación detallada del algoritmo KMP

Coincidencia de cuerdas optimizada para el ojo humano

Usamos los punteros de posición iyj en la cadena para ilustrar, el índice de la primera posición comienza con 0, lo llamamos la posición 0. Si se busca artificialmente, definitivamente no volveré al primer lugar, porque la posición fallida de coincidencia de la cadena principal (i = 3) no hay más A además de la primera A, ¿por qué podemos saber la principal? Solo hay una ¿A delante de la cuerda? ¡Porque ya sabemos que los tres primeros caracteres coinciden! (Esto es muy importante). ¡Moverse en el pasado definitivamente no es un partido! Hay una idea, no puedo moverme, solo necesitamos mover j, como se muestra a continuación:

Inserte la descripción de la imagen aquí
Las vacas grandes no pudieron soportar el método ineficaz de "agrietamiento por fuerza bruta", por lo que las tres desarrollaron el algoritmo KMP. La idea es la misma que vimos anteriormente: "Utilizando la información válida que se ha emparejado parcialmente, evitando que el puntero i retroceda y modificando el puntero j, la cadena del patrón se mueve a una posición válida tanto como sea posible".

Por lo tanto, el punto clave de todo el KMP es que cuando un determinado carácter no coincide con la cadena principal, ¿deberíamos saber dónde mover el puntero j?

A continuación, descubramos la ley de movimiento de j por nosotros mismos:

Inserte la descripción de la imagen aquí

Como se muestra en la figura: C y D no coinciden, ¿dónde queremos mover j? Obviamente el primer lugar. ¿Por qué? Porque hay una A delante del mismo.

Inserte la descripción de la imagen aquí

La misma situación se muestra en la siguiente figura:

Inserte la descripción de la imagen aquí

Puede mover el puntero j a la segunda posición, porque las dos letras al frente son iguales:

Inserte la descripción de la imagen aquí

En este punto, probablemente podamos ver una pista, cuando el partido falla, la siguiente posición k será movida por j. Existe tal propiedad: los primeros k caracteres son los mismos que los últimos k caracteres antes de j.

Si usa una fórmula matemática para expresarlo así

P [0 ~ k-1] == P [jk ~ j-1]

Esto es muy importante. Si te cuesta recordarlo, puedes entenderlo a través de la siguiente figura:

Inserte la descripción de la imagen aquí
Después de comprender esto, debería ser posible comprender por qué j se puede mover directamente a la posición k.

porque:

Cuando T [i]! = P [j]

Tener T [ij ~ i-1] == P [0 ~ j-1]

De P [0 ~ k-1] == P [jk ~ j-1]

Inevitablemente: T [ik ~ i-1] == P [0 ~ k-1]

La naturaleza del algoritmo KMP

El algoritmo KMP utiliza esta propiedad de la subcadena a emparejar para mejorar la velocidad de coincidencia. La naturaleza de muchas otras explicaciones de versiones también se puede describir como: si los prefijos y sufijos de las subcadenas repiten la subcadena más larga de longitud k, las siguientes subcadenas j coincidentes pueden moverse al k-ésimo bit (el subíndice es 0 para la posición 0). Definimos esta interpretación como la interpretación de subcadena máxima repetida.

En "aba", el conjunto de prefijos es el conjunto de subcadenas {a, ab} después de eliminar el último carácter 'a', y el conjunto de sufijos es el conjunto de subcadenas {a, ba} después de eliminar el primer carácter a. Luego, el la subcadena repetida más larga de las dos es a, k = 1;

Los pasos descompuestos en una computadora son los siguientes:

1) Busque el prefijo pre y ajústelo a pre [0 ~ m];

2) Busque el sufijo post y configúrelo como post [0 ~ n];

3) Desde el prefijo pre, primero use la longitud máxima s [0 ~ m] como una subcadena, es decir, establezca el valor inicial de k am, y compárelo con post [n-m + 1 ~ n]:

Si son iguales, entonces pre [0 ~ m] es la subcadena repetida más grande, y la longitud es m, entonces k = m;

Si no son iguales, entonces k = k-1; un carácter de la subcadena del prefijo reducido se alinea con la subcadena del sufijo según la cola, y se realiza la comparación para ver si son iguales .

Esto continúa hasta que se encuentra una subcadena duplicada o no se encuentra k.

Encuentra la siguiente matriz

Bien, el siguiente paso es el punto ¿Cómo encontramos este (estos) k? Debido a que pueden ocurrir desajustes en cada posición de P, es decir, tenemos que calcular la k correspondiente a cada posición j, por lo que usamos una matriz al lado para guardar, siguiente [j] = k, lo que significa cuando T [i] ! = Cuando P [j], la siguiente posición del puntero j.
Otra definición muy útil e idéntica, debido a que el subíndice comienza desde 0, el valor de k es en realidad la longitud de la subcadena máxima repetida de la subcadena antes del bit j. Tenga en cuenta la definición de la siguiente matriz en todo momento. La siguiente explicación se basa en esta definición.

Ejemplo de código 1

void Getnext(int next[],String t) {
    
    
   int j=0,k=-1;
   next[0]=-1;
   while(j<t.length-1) {
    
    
      if(k == -1 || t[j] == t[k]) next[++j] = ++k;
      else k = next[k];
   }
}

Veamos primero el primero: cuando j es 0, ¿qué pasa si no hay ninguna coincidencia en este momento?

Inserte la descripción de la imagen aquí

En el caso de la imagen de arriba, j ya está en el extremo izquierdo y es imposible moverlo. En este momento, el puntero i debería moverse hacia atrás. Entonces habrá next [0] = -1; esta inicialización en el código.

¿Y si es cuando j es 1?
Obviamente, el puntero j debe volver a colocarse en la posición 0. Porque solo hay este lugar frente a él ~~~

Inserte la descripción de la imagen aquí

Lo siguiente es lo más importante, consulte la imagen a continuación:

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Compare estas dos cifras con cuidado.

Encontramos una regla:

Cuando P [k] == P [j],

有 siguiente [j + 1] == siguiente [j] + 1

¿Y si P [k]! = P [j]? Por ejemplo, como se muestra en la siguiente figura:

Inserte la descripción de la imagen aquí

En este caso, si miras el código, debería ser esta oración: k = siguiente [k], ¿por qué es así? Debería entenderlo mirando lo siguiente.

Inserte la descripción de la imagen aquí

¡Ahora debería saber por qué k = siguiente [k]! Como en el ejemplo anterior, ya no podemos encontrar la cadena de sufijo más larga [A, B, A, B], pero aún podemos encontrar cadenas de prefijo como [A, B] y [B]. Entonces, este proceso no parece colocar la cadena [A, B, A, C], cuando C es diferente de la cadena principal (es decir, la posición de k es diferente), entonces, por supuesto, el puntero se mueve a la siguiente [ k]..

Punto de memoria

1) El valor de k es la longitud de la subcadena máxima repetida de la subcadena antes de j bits.

2) La siguiente matriz se guarda, y cada posición j corresponde a k

Optimización del siguiente algoritmo de resolución de matrices

Finalmente, observe las fallas en el algoritmo anterior. Mira el primer ejemplo:

Inserte la descripción de la imagen aquí

Obviamente, cuando obtenemos la siguiente matriz del algoritmo anterior, debería ser [-1, 0, 0, 1]

Entonces, el siguiente paso es mover j al primer elemento:

Inserte la descripción de la imagen aquí

No es difícil darse cuenta de que este paso no tiene ningún sentido. Debido a que el último B ya no coincide, el primero B tampoco debe coincidir. La misma situación ocurre en el segundo elemento A.

Obviamente, la razón del problema es que P [j] == P [siguiente [j]].

Ejemplo de código modificado

void Getnext(int next[],String t)
{
    
    
   int j=0,k=-1;
   next[0]=-1;
   while(j<t.length-1) {
    
    
      if(k == -1 || t[j] == t[k]) {
    
    
         if(t[++j]==t[++k])//当两个字符相同时,就跳过
            next[j] = next[k];
         else
            next[j] = k;
      }
      else k = next[k];
   }
}

Algoritmo KMP

int KMP(String s,String t)
{
    
    
   int next[MaxSize],i=0;j=0;
   Getnext(t,next);
   while(i<s.length&&j<t.length) {
    
    
      if(j==-1 || s[i]==t[j]) {
    
    
         i++;
         j++;
      }
      else j=next[j];               //j回退
   }
   if(j>=t.length)
       return (i-t.length);         //匹配成功,返回子串的位置
   else
      return (-1);                  //没找到
}

Publicación de blog de referencia: https://www.cnblogs.com/dusf/p/kmp.html
https://blog.csdn.net/dark_cy/article/details/88698736

Supongo que te gusta

Origin blog.csdn.net/Bluesky_lt/article/details/113386079
Recomendado
Clasificación