Explication détaillée de l'algorithme KMP avec des questions pratiques

Algorithme KMP

1. Brève introduction

L'algorithme KMP est utilisé pour la mise en correspondance des chaînes afin de renvoyer la position de départ de la chaîne mise en correspondance avec succès, et la complexité temporelle est O(N)

La fonction indexOf est fournie avec Java et la fonction indexOf est une version optimisée de KMP, qui optimise uniquement le temps constant.

2.tableau suivant

effet

  • Il peut accélérer le processus de correspondance sans correspondance violente
  • Le tableau suivant enregistre la longueur maximale correspondante de la chaîne de préfixe et de la chaîne de suffixe (sans compter la chaîne elle-même)

Processus de mise en œuvre

  • next[0]La valeur par défaut est -1, qui est artificiellement spécifiée et utilisée pour les jugements ultérieurs
  • next[1]=0, i=1lorsqu'il [0,i-1]n'y a qu'un seul caractère dans la plage, la longueur du préfixe et la longueur du suffixe sont donc 0, car les longueurs du préfixe et du suffixe ne s'incluent pas lors du calcul
  • En parcourant la chaîne depuis i=2le début, il existe trois situations générales :
    • Cas 1 : i-1Le caractère à la position est égal à la position de départ du préfixe à rechercher, next[i]égal à la position de départ du préfixe plus 1, l'expressionnext[i]=++index
    • Cas 2 : si le préfixe et le suffixe ne correspondent pas correctement, nextrecherchez la position de préfixe correspondante dans l'indice d'index du tableau et l'expressionindex=next[index]
    • Cas 3 : le préfixe et le suffixe ne correspondent pas correctement et le tableau suivant ne peut plus rechercher de valeurs.next[i]=0

Processus de mise en œuvre graphique de la prochaine baie
sous-chaîne cibleinsérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici

code de tableau suivant

   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. Fonction de comparaison de chaîne principale et de sous-chaîne

processus

  • Appelez la fonction getNext pour obtenir le prochain tableau de sous-chaînes
  • Utilisez iet jcomme indices pour parcourir la chaîne principale str1et la sous-chaîne respectivementstr2
  • Il y a trois cas où ni ietj
  • Cas 1 : le caractère à la position actuelle de la chaîne principale est égal au caractère à la position actuelle de la sous-chaîne, iet jles deux sommes sont incrémentées
  • Situation 2 : lorsque le tableau suivant de la sous-chaîne est égal à -1, c'est-à-dire next[0]la valeur spécifiée artificiellement, ou jégale à 0, cela signifie que la correspondance a échoué et iqu'elle sera incrémentée, jen gardant 0 inchangé
  • Cas 3 : le caractère à la position actuelle de la chaîne principale n'est pas égal au caractère à la position actuelle de la sous-chaîne. À ce stade, j>0recherchez la position du préfixe précédent dans le tableau suivant
  • La dernière jvaleur à vérifier est si elle est égale à la longueur de la sous-chaîne. Si elle est égale à la longueur de la sous-chaîne, cela signifie que la correspondance est réussie, puis revenir signifie que la correspondance commence à partir de la position de la i-jchaîne i-jprincipale chaîne.
  • Renvoie -1 si la correspondance échoue

le code

   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. Code global

#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. Sujets connexes sur KMP

Nombre minimum de caractères à ajouter

Étant donné une chaîne str, vous ne pouvez ajouter des caractères qu'après str pour générer une chaîne plus longue. La chaîne la plus longue doit contenir deux chaînes, et les positions de départ des deux chaînes ne peuvent pas être identiques. Trouvez le nombre minimum de caractères à ajouter.

Description de l'entrée :
entrez une ligne indiquant la chaîne d'origine

Description de la sortie :
génère un entier, indiquant le nombre minimum de caractères à ajouter

Exemple 1
entrée
123123
sortie
3

Exemple 2
entrée
11111
sortie
1

train de pensée

  • Selon le sens de la question, il y a trois situations
    • Cas 1 : Un caractère, la réponse est 1, ajoutez simplement un caractère
    • Situation 2: Deux caractères, jugez si les deux caractères sont identiques, s'ils sont identiques, la réponse est la longueur de la chaîne, car cette chaîne doit être ajoutée, s'ils sont différents, la réponse est 1, utilisez simplement le premier caractère il suffit de l'ajouter
    • Cas 3 : plusieurs caractères, le tableau suivant à la dernière position de la chaîne, car la signification du tableau suivant est la longueur de correspondance maximale entre le préfixe et le suffixe. La réponse est donc la longueur de la chaîne next[str.length()]moins 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;
}

article recommandé

Explication détaillée de l'algorithme de Mancher avec des questions pratiques

Je suppose que tu aimes

Origine blog.csdn.net/weixin_44839362/article/details/117134881
conseillé
Classement