Structure des données - algorithme de correspondance de modèles de chaîne (algorithme kmp)

L'opération de positionnement d'une sous-chaîne est généralement appelée correspondance de modèle de chaîne

Algorithme de correspondance de modèles naïf

  1. Utilisez chaque caractère de la chaîne principale comme début de la sous-chaîne pour correspondre à la chaîne à mettre en correspondance.
  2. Créez une grande boucle pour la chaîne principale et utilisez le début de chaque caractère comme une petite boucle pour la longueur de la sous-chaîne jusqu'à ce que la correspondance soit réussie ou que tout le parcours soit terminé.
/*返回子串T在主串s中第pos个字符之后的位置。若不存在,则函数返回值为0。*/
/*T非空,1≤pos ≤StrLength(S)。*/
int Index(String S, String T, int pos)
{
    
    
	int i = pos;/*i用于主串S中当前位置下标,若pos不为1*/
			  /*则从pos 位置开始匹配*/
	int j = 1; /*j用于子串T中当前位置下标值*/
	while(i <= S[0] && j <= T[0])/*若i小于S长度且j小于T的长度时循环*/
	{
    
    
		if(S[i] == T[j]) /*两字母相等则继续*/
		{
    
    
			++i;
			++j;
		}  
		else /*指针后退重新开始匹配*/
		{
    
     
			i = i-j+2; /*i退回到上次匹配首位的下一位*/
						//串是从 1 开始计数的
						//i-j+1 恰好回到这次匹配的第一个字符
						//所以 (i-j+1)+1 指到下一个字符
						//从而开始一轮新的匹配
			j = 1;
		} 
	}
	if(j > T[0]){
    
    
		return i-T[0];
	}
	else
	{
    
    
		return 0;
	}
}

Complexité temporelle : O(n*m)
Cela prend trop de temps et n’est généralement pas recommandé.

Algorithme de correspondance de modèles KMP

Il y a trois prédécesseurs, DEKnuth, JHMorris et VRPratt (Knuth et Pratt ont fait des recherches conjointes, Morris Independent Research) ont publié un algorithme de correspondance de modèles, qui peut grandement éviter la situation de traversée répétée, nous l'appelons l'algorithme spécial de Knuth-Morris-Pratt, en référence à comme algorithme KMP .

Dans l'algorithme KMP, seule la position de la sous-chaîne doit changer d'avant en arrière et la chaîne principale recule.

  1. Certains chiffres de la chaîne à mettre en correspondance ne sont pas égaux les uns aux autres, et ils ont des sous-chaînes correspondantes dans la chaîne principale. Lorsque ce dernier chiffre ne correspond pas, ces chiffres de la chaîne à mettre en correspondance n'ont pas besoin d'être manifestement inégaux entre eux. dans la chaîne principale caractères à comparer ;
  2. Il y a une égalité de palindrome dans la chaîne à mettre en correspondance, et ils ont des sous-chaînes correspondantes dans la chaîne principale. Lorsque ce dernier chiffre ne correspond pas, les premiers chiffres de la chaîne de palindrome dans la chaîne à mettre en correspondance n'ont pas besoin d'être évidemment égaux à lui-même dans les caractères de la chaîne principale à comparer.
  3. Consommation de temps considérablement réduite, sonComplexité temporelle O(n+m);

Trouvez la position de retour en arrière d'une sous-chaîne :

/*通过计算返回子串T的next数组。*/
void get_next(String T, int *next)
{
    
    
	int i,j;
	i = 1;
	j = 0;
	next[1] = 0;
	while(i < T[0]) /*此处T[0]表示串T的长度*/
	if(j == 0 || T[i] == T[j])/*T[i]表示后缓的单个字符,*/
							  /* T[j]表示前缀的单个字符 */
	{
    
    
		++i;
		++j;
		next[i]=j;	
	}
	else
	{
    
    
		j= next[j];/* 若字符不相同,则j值回溯*/
	}
}

Code maître

// 返回子串T在主串S中第pos个字符之后的位置。
// 若不存在,则函数返回值为0
// T非空,1<=pos<=StrLength(S)
int Index_KMP(String S, String T, int pos){
    
    
    // i 用于主串S当前位置下标值,若pos不为1,则从pos位置开始匹配
    int i = pos;
    int j = 1;//j 用于子串T中当前位置下标值
    int next[255];// 定义next数组
    get_next(T,next);// 对串T作分析,得到next数组
    // 若i小于S的长度且j小于T的长度时,循环继续
    while(i <= S[0] && j <= T[0]){
    
    
        // 两字母相等则继续,相对于朴素算法增加了j=0 判断
        if(j == 0 || S[i] == T[j]){
    
    
            ++i;
            ++j;
        }
        // 指针后退重新开始匹配
        else{
    
    
            // j退回合适的位置,i值不变
            j = next[j];
        }
    }
    if(j>T[0]){
    
    
        return i-T[0];
    }
    else
    {
    
    
        return 0;
    }
}

Pièce jointe :
Améliorations pour trouver le tableau suivant :

// 求模式串T的next函数修正值并存入数组nextval
void get_nextval(String T,int *nextval){
    
    
    int i,j;
    i = 1;
    j = 0;
    nextval[1] = 0;
    while(i < T[0]){
    
    
        if(j == 0 || T[i] == T[j]){
    
    
            i++;
            j++;
            if(T[i] != T[j])// 若当前字符与前缀字符不同
            {
    
    
				 nextval[i] = j;// 则当前的j为nextval在i位置的值
			}  
            else
            {
    
    
				nextval[i] = nextval[j];
				//如果与前缀字符相同,则将前缀字符的nextval值赋值给nextval在i位置的值
			}  
        }
        else
        {
    
    
	        //若字符不相同,则j值回溯
	         j=nextval[j];
        }
    }
}

Si vous n’arrivez vraiment pas à comprendre, mémorisez-le !

Je suppose que tu aimes

Origine blog.csdn.net/The_onion/article/details/123759631
conseillé
Classement