Next 數組及KMP

Next 數組及KMP

KMP算法的核心是利用匹配失敗後的信息,儘可能減小模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是經過一個getnext()函數實現,函數自己包含了模式串的局部匹配信息。KMP算法的時間複雜度O(m+n)。其中最難理解的部分即next數組的求法以及回溯c++

前綴數組

**前綴數組中存儲的是這個字符串前綴和後綴中相同子串的最長長度 **web

next 數組考慮的是除當前字符外的最長相同前綴後綴算法

以 agctagcagctagctg 爲例數組

** start**svg

將next數組初始化以後爲:函數

在這裏插入圖片描述

–>將模串依次向後移比較至此測試

在這裏插入圖片描述

–>回溯code

此時對比,小串長爲3,j=3;回溯,j=next[j=3]=0;此時i=7;繼續匹配依次後移xml

在這裏插入圖片描述

–>再回溯blog

此時對比,小串長爲3,j=7;回溯,j=next[j]=3,i=14;繼續匹配,當pat[i=14]=pat[j=3],i++,j++,即next[15]=4;

在這裏插入圖片描述

–>j=next[j=4]=0;–>j=next[j=0]=-1;–>i++=,j++,next[i=16]=j=0;

** end**

//獲取前綴數組
void GetNext(char *pat){
    int i=0,j=-1;
    next[0]=-1;
    while(i<n){                        //n爲模板串pat的長度
        if(j==-1||pat[i]==pat[j]){
            i++,j++;
            next[i]=j;
        }
        else
            j=next[j];
    }
}




//第二種next數組
//  abcab
//  00012
void calc_next() {
	next[0] = 0;         //下標從1開始  next[1]=0;
	for (int i = 1, j = 0; i < n; i++) { //i=2;i<=n
        while (j > 0 && pat[i] != pat[j])//pat[i]!=pat[j+1]
			j = next[j - 1];             //j=next[j]
		if (pat[i] == pat[j])            //pat[i]==pat[j+1]
			j++;
		next[i] = j;
	}
}

獻上一道前綴數組尋找循環節的問題

題號:hdu-1358

題意:一個字符串的前綴是從第一個字符開始的連續若干個字符,例如」abaab」共有5個前綴,分別是a, ab, aba, abaa, abaab。咱們但願知道一個N位字符串S的前綴是否具備循環節。換言之,對於每 一個從頭開始的長度爲 i (i>1)的前綴,是否由重複出現的子串A組成,即 AAA…A (A重複出 現K次,K>1)。若是存在,請找出最短的循環節對應的K值(也就是這個前綴串的全部可能重複節 中,最大的K值)。

輸入:輸入包括多組測試數據,每組測試數據包括兩行。

​    第一行輸入字符串S的長度N。

​    第二行輸入字符串S。

​    輸入數據以只包括一個0的行做爲結尾。

輸出:對於每組測試數據,第一行輸出 「Test case #」 和測試數據的編號。

​    接下來的每一行,輸出具備循環節的前綴的長度i和其對應K,中間用一個空格隔開。

​    前綴長度須要升序排列。

​    在每組測試數據的最後輸出一個空行。

#include<stdio.h>
#include<string.h>
const int maxn = 1000000 + 5;
char pat[maxn];
int next[maxn];
int len;
void getnext() {
	int i = 0, j = -1;
	next[0] = -1;
	while (i < len) {
		if (j == -1 || pat[i] == pat[j]) {
			i++, j++;
			next[i] = j;
		}
		else
			j = next[j];
	}
}
int main()
{
	int t = 0;
	while (scanf("%d", &len) != EOF && len) {
		memset(pat, '\0', sizeof(pat));
		scanf("%s", pat);
		getnext();
		printf("Test case #%d\n", ++t);
		for (int i = 2; i <= len; i++) {
			if (!(i % (i - next[i])) && i / (i - next[i]) != 1)
				printf("%d %d\n", i, i / (i - next[i]));
		}
		printf("\n");
	}
	return 0;
}

KMP 匹配

理解了怎麼得到前綴數組,那麼咱們來淺談KMP的匹配模式:

假設如今文本串S匹配到 i 位置,模式串P匹配到 j 位置

若是j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++,繼續匹配下一個字符;

在這裏插入圖片描述

若是j != -1, 且當前字符串匹配失敗(即s[i] != p[i]),則令i不變,j = next[j] 。此時意味着匹配失敗,模板串pat相對於主串str向右移 j-next[j]。

在這裏插入圖片描述

void KMP()
{
	GetNext(pat);
	int i = 0, j = 0;
	int sum = 0;
	while (i < m)                       //m爲主串str長度
	{
		if (j == -1 || str[i] == pat[j])
			i++, j++;
		else
			j = Next[j];
		if (j == n)                     //當前模板串pat與主串str其中一部分匹配成功
            //根據題意添加條件
	}
	return ;
}
相關文章
相關標籤/搜索