Explicación detallada del gráfico del algoritmo KMP

Algoritmo KMP detallado

Primero explique qué algoritmo KMP:

El problema que debe resolver el algoritmo KMP es ubicar el patrón en la cadena (también llamada cadena principal). En pocas palabras, es la búsqueda de palabras clave que solemos decir. La cadena del patrón es la palabra clave (llamada P en el siguiente), si aparece en una cadena principal (llamada T en el siguiente), devuelve su posición específica; de lo contrario, devuelve -1 (medio de uso común)

Sobre la solución de este tipo de problema

  • Método de fuerza bruta, complejidad temporal O ( N ∗ MN * MnorteM ) (N es la longitud de la cadena principal, M es la longitud de la cadena del patrón), la cadena del patrón intenta hacer coincidir cada posición de la cadena principal hasta que la coincidencia es exitosa.
    Por ejemplo:
    cuando dos cadenas coinciden, el La cadena P comienza desde la cadena principal T El primer carácter comienza a coincidir y la posición roja es la posición donde el primer carácter no coincide. En este momento, el subíndice del carácter no
    Inserte la descripción de la imagen aquí
    coincidentees i = j = 3. Lasiguiente coincidencia es que P comienza desde el subíndice 0 y T desde el subíndice 1 para coincidir, y luego repite este proceso.
    Inserte la descripción de la imagen aquí
    Este método es demasiado violento, por lo que tenemos para introducir un método más rápido, elalgoritmo KMP
  • Complejidad de tiempo del algoritmo KMP O ( NNN ) La longitud de la cadena principal de N está
    de acuerdo con el pensamiento normal. Para la coincidencia en la figura anterior, cuando ocurre una falta de coincidencia por primera vez, es natural pensar en la a en la cadena P y el subíndice 3 en la cadena T, es decir, caracteres coincidentes, consulte la figura
    Inserte la descripción de la imagen aquí
    , es decir, la cadena del subíndice T i constante, la cadena P j se vuelve cero, es decir, la ventajacon esta coincidencia parcial tiene información válida, el puntero i no se detiene modificando el puntero j, por lo que que el patrón de cadena Intente moverse a una ubicación válida tanto como sea posible.
    Veamos un conjunto de datos nuevamente.
    Inserte la descripción de la imagen aquí
    Cuando T y P no coinciden en el subíndice 3, de acuerdo con lo anterior, i no se mueve, y la cadena P se mueve a la posición válida. La próxima vez que comience la comparación, el posición es j = 2.
    Inserte la descripción de la imagen aquí
    De hecho, esto es como un proceso de empuje: laposición de j está determinada porel valor del sufijo de prefijo común más largo en la cadenaantesdeldesajuste entre P y T.

Primero, explica el prefijo y el sufijo. Toma la cadena como ejemplo. El
Inserte la descripción de la imagen aquí
prefijo son todas las subcadenas consecutivas que contienen el primer carácter, pero no el último , como se muestra en la figura siguiente. El
Inserte la descripción de la imagen aquí
sufijo es el que contiene el último carácter y no incluye el primero Todas las subcadenas consecutivas de caracteres
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
Aquí quiero encontrar una siguiente matriz para la cadena de patrón , la siguiente [i] representa el valor del sufijo de prefijo común más largo de los primeros caracteres i-1. Regulaciones artificiales next [0] = - 1, next [1] = 0;

  • Porque cuando solo hay un carácter, no hay ningún carácter antes, por lo que no tiene sentido.
  • Cuando hay dos caracteres, tome la cadena P anterior como ejemplo, el siguiente [1] es encontrar el valor del sufijo de prefijo común más largo de a antes de b, pero debido a que solo hay un carácter, el prefijo no incluye el último carácter y El sufijo no contiene el primer carácter, contradictorio, por lo que el valor es 0.

Para next [6], el prefijo y sufijo común más largo del abcabc frontal es abc, entonces next [6] = 3

Cómo resolver el siguiente arreglo
Empiece con el siguiente [2] Como se mencionó anteriormente, las posiciones 0 y 1 son valores especificados artificialmente. A continuación se muestra el método de solución del siguiente [i] (i> = 2).

  • Primero, se debe especificar aquí un valor de k El valor de k representa el valor del siguiente [i-1], que es el valor del prefijo y sufijo común más largo en los primeros i-2 caracteres.
  • Al resolver la siguiente [i], es necesario comparar si el carácter i-1º y el carácter k son iguales
    • Cuando es igual, siguiente [i] = siguiente [i-1] +1;
    • De lo contrario, k = siguiente [k], hasta que k = 0;

Por ejemplo:
Inserte la descripción de la imagen aquí
valor inicial k = 0; nex [0] = - 1, siguiente [1] = 0; cuando
Inserte la descripción de la imagen aquí
i = 2, p [1]! = P [k] (k = 0) significa b! = A
k = 0 // Es decir, ha llegado al final y aún no se ha emparejado. Porque k == 0, sea next [2] = 0; cuando
Inserte la descripción de la imagen aquí
i = 3, p [2]! = P [k] (k = 0), porque k = = 0, por lo que sigue [3] = 0, cuando
Inserte la descripción de la imagen aquí
i = 4 al final , p [3] == p [k] (k = 0), así que sigue [4 ] = ++ k; Es decir, siguiente [4] = 1; cuando
Inserte la descripción de la imagen aquí
i = 5, p [4] = p [k] (k = 1), entonces siguiente [5] = ++ k; es decir, siguiente [5 ] = 2; lo
Inserte la descripción de la imagen aquí
mismo Obtener siguiente [6] = 3
Inserte la descripción de la imagen aquí

Pero solo me enteré después de ejecutarlo nuevamente. . . El uso de k = next [k] no se usa en este ejemplo. Luego voy a modificar la cadena P para que c con el subíndice 5 se convierta en a. Cuando
Inserte la descripción de la imagen aquí
i = 6, k = 2, p [6]! = P [2], k = next [k], es decir k = 0
p [0] == p [6], entonces siguiente [6] = ++ k = 1;

Puede dividir P en dos partes y mirar la
Inserte la descripción de la imagen aquí
siguiente matriz.
Lo siguiente es la parte del algoritmo KMP

  • Ingrese dos cadenas s1, s2 (s1 es la cadena principal, s2 es la cadena del patrón)
  • Obtenga la siguiente matriz de s2
  • Defina dos punteros i1 e i2 para representar la posición de s1 y la posición de s2 respectivamente.
  • Cuando s1 [i1] == s2 [i2], i1 ++, i2 ++
  • De lo contrario, i2 = next [i2]
  • Cuando i2 se mueve a 0, es decir, siguiente [i2] == - 1, i1 ++ coincide con el primero de la cadena de patrón y no coincide, y solo puede coincidir con el siguiente de la cadena principal.
  • Finalmente, juzgue si i2 es igual a la longitud de s2, que es igual a devolver i1-i2; de lo contrario, devolver -1

Por ejemplo,
Inserte la descripción de la imagen aquí
se ha calculado la siguiente matriz de cadena P, y las dos cadenas son diferentes cuando i1 = i2 = 6. En este momento, i1 no se mueve, i2 = siguiente [6] = 3. Como se muestra en la figura siguiente
Inserte la descripción de la imagen aquí
, la coincidencia puede realizarse correctamente y devolver i1 -i2 = 3.

A continuación se muestra el código ()

Encuentra la siguiente matriz

void get_next()
{
    
    
    Next[0]=-1;
    Next[1]=0;

    int i=2,k=0;//i是模式串的起始位置,即从第三个字符开始匹配,k是i-1个字符要匹配的位置
    int len=s2.size();
    while(i<len)
    {
    
    
        if(s2[i-1]==s2[k])//如果i-1和k相等,i后移准备匹配下一个位置,k后移
            Next[i++]=++k;
        else if(k>0)//没有匹配成功,k移动到next[k]的位置
            k=Next[k];
        else
            Next[i++]=0;//移动到头了,next[i]只能为0了
    }
}

km²

int kmp()
{
    
    
    int i1=0,i2=0;
    int len1=s1.size();
    int len2=s2.size();
    get_next();//获得Next数组
    while(i1<len1&&i2<len2)//i1没到头,i2也没到头
    {
    
    
        if(s1[i1]==s2[i2])//相等就齐头并进
        {
    
    
            i1++;
            i2++;
        }
        else if(next[i2]==-1)//模式串到头都没有和主串能匹配的字符,主串往后移
            i1++;
        else
            i2=next[i2];//匹配不成功,i2移动
    }
    return i2==len2?i1-i2:-1;//i2到头证明匹配成功,否则返回-1
}

Pregunta de plantilla: HDU-1711
Pit: Este es un número, no un carácter, use una matriz de enteros para recibir el
código AC

#include<iostream>
#include<cstdio>
#include<string.h>
#include<queue>
#include<cmath>
#include<fstream>
using namespace std;

int Next[200005];
int s1[1000005];
int s2[1000005];
int a,b;
void get_next()
{
    
    
    Next[0]=-1;
    Next[1]=0;

    int i=2,k=0;//i是模式串的起始位置,即从第三个字符开始匹配,k是i-1个字符要匹配的位置
    int len=b;
    while(i<len)
    {
    
    
        if(s2[i-1]==s2[k])//如果i-1和k相等,i后移准备匹配下一个位置,k后移
            Next[i++]=++k;
        else if(k>0)//没有匹配成功,k移动到next[k]的位置
            k=Next[k];
        else
            Next[i++]=0;//移动到头了,next[i]只能为0了
    }
}

int kmp()
{
    
    
    int i1=0,i2=0;
    get_next();
    int len1=a;
    int len2=b;

    while(i2<len2&&i1<len1)//i1没到头,i2也没到头
    {
    
    

        if(s1[i1]==s2[i2])//相等就齐头并进
        {
    
    
            i1++;
            i2++;
        }
        else if(Next[i2]==-1)//模式串到头都没有和主串能匹配的字符,主串往后移
            i1++;
        else
            i2=Next[i2];//匹配不成功,i2移动

    }
    return i2==len2?i1-i2:-1;//i2到头证明匹配成功,否则返回-1
}

int main(void)
{
    
    
    int t;
    cin>>t;

    while(t--)
    {
    
    
        //memset(Next,0,sizeof(Next));

        int ans=kmp();
        if(ans!=-1)
            cout<<ans+1<<endl;
        else
            cout<<ans<<endl;
    }
    return 0;
}

Punto de
pozo de Logu P3375 : la siguiente matriz no contiene el valor de sufijo de prefijo más largo de los primeros n-1 caracteres, por lo que agrego un carácter inútil al principio y luego genera la siguiente matriz de 1 al generar.
Código de CA

#include<iostream>
#include<cstdio>
#include<string.h>
#include<queue>
#include<cmath>
using namespace std;

int next[2000005];
string s1,s2;
void get_next()
{
    
    
    next[0]=-1;
    next[1]=0;

    int i=2,k=0;//i是模式串的起始位置,即从第三个字符开始匹配,k是i-1个字符要匹配的位置
    int len=s2.size();
    while(i<len)
    {
    
    
        if(s2[i-1]==s2[k])//如果i-1和k相等,i后移准备匹配下一个位置,k后移
            next[i++]=++k;
        else if(k>0)//没有匹配成功,k移动到next[k]的位置
            k=next[k];
        else
            next[i++]=0;//移动到头了,next[i]只能为0了
    }
}

int kmp()
{
    
    
    int i1=0,i2=0;
    get_next();
    s2=s2.substr(0,s2.size()-1);
    int len1=s1.size();
    int len2=s2.size();

    while(i1<len1)//i1没到头,i2也没到头
    {
    
    


        if(s1[i1]==s2[i2])//相等就齐头并进
        {
    
    
            i1++;
            i2++;
        }
        else if(next[i2]==-1)//模式串到头都没有和主串能匹配的字符,主串往后移
            i1++;
        else
            i2=next[i2];//匹配不成功,i2移动
        if(i2==len2)
        {
    
    
            printf("%d\n",i1-i2+1);
            i2=next[i2]; //再次匹配
            i1--;
        }

    }
    //return i2==len2?i1-i2:-1;//i2到头证明匹配成功,否则返回-1
}

int main(void)
{
    
    

    cin>>s1>>s2;
    s2+="$";
    kmp();
    next[0]++;

    int len=s2.size();
    for(int i=1;i<=len;i++)
    {
    
    
        printf("%d ",next[i]);
    }
    return 0;
}

Lo escribí toda la tarde, pero está terminado. Por favor, apúnteme si lo ha visto, gracias

Supongo que te gusta

Origin blog.csdn.net/Yang_1998/article/details/89764554
Recomendado
Clasificación