Algoritmo KMP
1. Breve introducción
El algoritmo KMP se usa para la coincidencia de cadenas para devolver la posición inicial de la cadena coincidente con éxito, y la complejidad de tiempo es O (N)
La función indexOf viene con Java, y la función indexOf es una versión optimizada de KMP, que solo optimiza el tiempo constante.
2.siguiente matriz
efecto
- Puede acelerar el proceso de emparejamiento sin emparejamiento violento.
- La siguiente matriz guarda la longitud máxima coincidente de la cadena de prefijo y la cadena de sufijo (sin incluir la cadena en sí)
Proceso de implementación
next[0]
El valor predeterminado es -1, que se especifica artificialmente y se utiliza para juicios posteriores.next[1]=0
,i=1
cuando[0,i-1]
solo hay un carácter en el rango, por lo que la longitud del prefijo y la longitud del sufijo son 0, porque las longitudes del prefijo y el sufijo no se incluyen al calcular- Recorriendo la cuerda desde
i=2
el principio, hay tres situaciones generales:- Caso 1:
i-1
el carácter en la posición es igual a la posición inicial del prefijo que se va a emparejar,next[i]
igual a la posición inicial del prefijo más 1, la expresiónnext[i]=++index
- Caso 2: si el prefijo y el sufijo no coinciden correctamente,
next
busque la posición del prefijo correspondiente en el subíndice de índice de la matriz y la expresiónindex=next[index]
- Caso 3: el prefijo y el sufijo no coinciden correctamente y la matriz siguiente ya no puede buscar valores.
next[i]=0
- Caso 1:
Proceso de implementación de matriz gráfica siguiente
siguiente código de matriz
vector<int> getNext(string str) {
// 每个位置字符串的前缀与后缀最大匹配长度,不包含整串
vector<int> next(str.size());
next[0] = -1; //人为规定,0号位置的值是-1
next[1] = 0;
int i = 2; // 从2开始遍历str
// index代表当前是哪个位置的字符,在和index+1也就是i位置比较
int index = 0; // index既用来作为下标访问,也作为值
while (i < next.size()) {
// str[i-1]代表后缀开始的位置, str[index]代表前缀开始的位置
// index保存了上一次匹配的最大长度, str[index]代表了当前前缀位置, 可以通过这个来进行加速匹配
if (str[i - 1] == str[index]) {
// 如果str[i-1](后缀待匹配的字符) 等于 str[index](前缀待匹配的字符)
// next数组i位置的值 直接等于上次最大匹配长度+1
next[i++] = ++index;
}
else if (index > 0) {
// 后缀与前缀没有匹配成功, 并且index还可以往前找 next[index]的前缀, 也就是找当前前缀的前缀开始位置
index = next[index];
}
else{
// index=0, 没有前缀了, 长度记为0
next[i++] = 0;
}
}
return next;
}
3. Función de comparación de cadena principal y subcadena
proceso
- Llame a la función getNext para obtener la siguiente matriz de subcadenas
- Use
i
yj
como subíndices para recorrer la cadena principalstr1
y la subcadena respectivamentestr2
- Hay tres casos en los que ninguno
i
yj
- Caso 1: el carácter en la posición actual de la cadena principal es igual al carácter en la posición actual de la subcadena,
i
yj
ambas sumas se incrementan - Situación 2: cuando la siguiente matriz de la subcadena es igual a -1, es decir,
next[0]
el valor especificado artificialmente, oj
igual a 0, significa que la coincidencia falló yi
se incrementará,j
manteniendo 0 sin cambios - Caso 3: El carácter en la posición actual de la cadena principal no es igual al carácter en la posición actual de la subcadena En este momento
j>0
, encuentre la posición del prefijo anterior en la siguiente matriz - El último
j
valor a verificar es si es igual a la longitud de la subcadena.Si es igual a la longitud de la subcadena, significa que la coincidencia es exitosa, y luego regresar significa que la coincidencia comienza desde la posición dei-j
lai-j
principal cadena. - Devuelve -1 si la coincidencia falla
el código
int getIndex(string str1, string str2) {
vector<int> next = getNext(str2);
int i = 0;
int j = 0;
while (i < str1.size() && j < str2.size()) {
if (str1[i] == str2[j]) {
i++;
j++;
}else if (next[j] == -1) {
i++;
}else{
j = next[j];
}
}
if (j == str2.size()) {
return i - j;
}
return -1;
}
4. Código general
#include<iostream>
#include<string>
#include<vector>
using namespace std;
vector<int> getNext(string str) {
// 每个位置字符串的前缀与后缀最大匹配长度,不包含整串
vector<int> next(str.size());
next[0] = -1; //人为规定,0号位置的值是-1
next[1] = 0;
int i = 2; // 从2开始遍历str
// index代表当前是哪个位置的字符,在和index+1也就是i位置比较
int index = 0; // index既用来作为下标访问,也作为值
while (i < next.size()) {
// str[i-1]代表后缀开始的位置, str[index]代表前缀开始的位置
// index保存了上一次匹配的最大长度, str[index]代表了当前前缀位置, 可以通过这个来进行加速匹配
if (str[i - 1] == str[index]) {
// 如果str[i-1](后缀待匹配的字符) 等于 str[index](前缀待匹配的字符)
// next数组i位置的值 直接等于上次最大匹配长度+1
next[i++] = ++index;
}
else if (index > 0) {
// 后缀与前缀没有匹配成功, 并且index还可以往前找 next[index]的前缀, 也就是找当前前缀的前缀开始位置
index = next[index];
}
else{
// index=0, 没有前缀了, 长度记为0
next[i++] = 0;
}
}
return next;
}
int getIndex(string str1, string str2) {
vector<int> next = getNext(str2);
int i = 0;
int j = 0;
while (i < str1.size() && j < str2.size()) {
if (str1[i] == str2[j]) {
i++;
j++;
}else if (next[j] == -1) {
i++;
}else{
j = next[j];
}
}
if (j == str2.size()) {
return i - j;
}
return -1;
}
int main() {
// 在str1中查找有没有子串str2
string str1 = "abbcabcccc";
string str2 = "abcabc";
//cin >> str1 >> str2;
int index = getIndex(str1, str2);
cout << index;
return 0;
}
5. Temas relacionados con KMP
Número mínimo de caracteres para agregar
Dada una cadena str, solo puede agregar caracteres después de str para generar una cadena más larga. La cadena más larga debe contener dos strs, y las posiciones iniciales de las dos strs no pueden ser las mismas. Encuentre el número mínimo de caracteres para agregar.
Descripción de entrada:
ingrese una línea que indique la cadena original
Descripción de la salida:
genera un número entero que indica el número mínimo de caracteres necesarios para agregar
Ejemplo 1
entrada
123123
salida
3
Ejemplo 2
entrada
11111
salida
1
tren de pensamiento
- De acuerdo con el significado de la pregunta, hay tres situaciones
- Caso 1: un carácter, la respuesta es 1, solo agregue un carácter
- Situación 2: dos caracteres, juzgue si los dos caracteres son iguales, si son iguales, la respuesta es la longitud de la cadena, porque esta cadena debe agregarse, si son diferentes, la respuesta es 1, solo use el primer caracter solo agrégalo
- Caso 3: varios caracteres, la siguiente matriz en la última posición de la cadena, porque el significado de la siguiente matriz es la longitud máxima coincidente entre el prefijo y el sufijo. Entonces la respuesta es la longitud de la cadena
next[str.length()]
menos 1
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int getNext(string str) {
vector<int> next(str.size());
next[0] = -1; //人为规定,0号位置的值是-1
next[1] = 0;
int i = 2; // 从2开始遍历str
int val = 0; // val既用来作为下标访问,也作为值
while (i < next.size()) {
if (str[i - 1] == str[val]) {
next[i++] = ++val;
}
else if (val > 0) {
val = next[val]; // 取出前一个next数组的值
}
else {
next[i++] = 0;
}
}
return next[str.size() - 1];
}
int main() {
string str;
cin >> str;
int ans = 0;
if (str.size() == 0) {
cout << 0;
return 0;
}
else if (str.size() == 1) {
ans = str.size() + str.size();
}
else if (str.size() == 2) {
ans = str[0] == str[1] ? str.size() + 1 : str.size() + str.size();
}
else {
int next = getNext(str);
ans = str.size() + str.size() -1 - next;
}
ans -= str.size();
cout << ans;
return 0;
}
artículo recomendado
Explicación detallada del algoritmo de Mancher con preguntas de práctica.