Структура данных — алгоритм сопоставления строкового образца (алгоритм kmp)

Операцию позиционирования подстроки обычно называют сопоставлением строки с образцом.

Наивный алгоритм сопоставления с образцом

  1. Используйте каждый символ основной строки в качестве начала подстроки, соответствующей строке, подлежащей сопоставлению.
  2. Создайте большой цикл для основной строки и используйте начало каждого символа в качестве небольшого цикла для длины подстроки до тех пор, пока совпадение не будет успешным или весь обход не будет завершен.
/*返回子串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;
	}
}

Временная сложность: O(n*m)
Это занимает слишком много времени и обычно не рекомендуется.

Алгоритм сопоставления с образцом KMP

Есть три предшественника: DEKnuth, JHMorris и VRPratt (совместные исследования Кнута и Пратта, независимое исследование Морриса) опубликовали алгоритм сопоставления с образцом, который позволяет значительно избежать ситуации повторного обхода, мы называем его Специальным алгоритмом Кнута-Морриса-Пратта , называемым как алгоритм КМП .

В алгоритме KMP нужно менять только положение подстроки вперед и назад, а основная строка перемещается назад.

  1. Некоторые цифры сопоставляемой строки не равны друг другу и имеют соответствующие подстроки в основной строке. Если последняя цифра не совпадает, эти цифры в сопоставляемой строке не обязательно должны быть явно не равны сами себе. в основной строке символы для сравнения;
  2. В строке, подлежащей сопоставлению, имеются равенства палиндромов, и они имеют соответствующие подстроки в основной строке. Если последняя цифра не совпадает, первые несколько цифр строки-палиндрома в строке, подлежащей сопоставлению, не обязательно должны быть явно равны сами себе. в основной строке символы для сравнения.
  3. Значительное сокращение затрат времени,Временная сложность O(n+m);

Найдите позицию возврата подстроки:

/*通过计算返回子串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值回溯*/
	}
}

Главный код

// 返回子串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;
    }
}

Вложение:
Улучшения в поиске следующего массива :

// 求模式串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];
        }
    }
}

Если вы действительно не можете понять, то запомните!

Guess you like

Origin blog.csdn.net/The_onion/article/details/123759631