字串模式匹配之KMP

2021-09-21 02:31:41 字數 2605 閱讀 1477

假設現在我們面臨這樣乙個問題:有乙個主串(文字串)s,和乙個模式串p,現在要查詢p在s中的位置,怎麼查詢呢?

如果使用暴力匹配的思路,並假設現在主串s匹配到 i 位置,模式串p匹配到 j 位置,則有:

public

static

intviolentmatch

(string s, string p)

else

}//匹配成功,返回模式串p在文字串s中的位置,否則返回-1

if(j == plen)

return i - j;

else

return-1

;}

舉個例子,如果給定文字串s=「bbc abcdab abcdabcdabde」,和模式串p=「abcdabd」,現在要拿模式串p去跟文字串s匹配,整個過程如下所示:

下面是對上面思想的一些解釋或者補充:

public

static

intkmpmatch

(string s, string p)

else}if

(j == plen)

return i - j;

else

return-1

;}

這只是乙個匹配失敗時的舉例,基於kmp的完整匹配過程會在後面給出。

對於某乙個字串,它的字首、字尾分別定義為:

例如:給定字串p 「abcba」,p的字首集合為,字首集合為 ;p的公共字首字尾只有乙個,所以p的最長公共字首字尾的長度為1;

對於2.3中給的例子,模式串p=「abcdabd」,其各個前子串的字首字尾分別如下**所示:

也就是說,模式串各個前子串對應的最長公共字首字尾長度表為(下簡稱《最大長度表》):

next 陣列的求法:最大長度表右移一位,next [0]賦值為-1;

對於2.3中給的例子,模式串p=「abcdabd」,其最大長度表和next 陣列表為:

首先j為0時, next [ j ] = -1;假設k= next [ j ];然後執行下面遞迴過程:

下面是一些補充或解釋:

(1)已知k=next [ j ],所以在pj前存在p0 p1, …, pk-1 = pj-k, pj-k+1, …, pj-1;

(2)現在要求next [ j+1 ],先判斷pk?=pj;如果相等,那麼pj+1前存在p0 p1, …, pk-1,pk = pj-k, pj-k+1, …, pj-1,pj長度為k+1的最大公共字首字尾,所以next[ j + 1 ] = k + 1;

(3)如果pk!=pj,就去找乙個長度更小的公共字首字尾;遞迴比較pnext [k]?=pj,如果相等,則next[ j + 1 ] = k + 1 = next [ k ] + 1;否則繼續讓k=next [k],然後比較pk!和pj;

求next陣列其實就是乙個動態規劃問題,dp方程為next[ j + 1 ] = next [ j ] + 1 ;

public

static

void

getnext

(string p,

int[

] next)

else

}}

用**重新計算下「abcdabd」的next 陣列:

右移2位後,b又跟c失配。事實上,因為在上一步的匹配中,已經得知p[3] = b,與s[3] = c失配,而右移兩位之後,讓p[ next[3] ] = p[1] = b 再跟s[3]匹配時,必然失配。問題出在哪呢?

問題出在:當出現p[j] = p[ next[j] ]時,這次比較是非必要的。為什麼呢?理由是:當p[j] != s[i] 時,下次匹配必然是p[ next [j]] 跟s[i]匹配,如果p[j] = p[ next[j] ],必然導致後一步匹配失敗(因為p[j]已經跟s[i]失配,然後你還用跟p[j]等同的值p[next[j]]去跟s[i]匹配,很顯然,必然失配),所以不能允許p[j] = p[ next[j ]]。如果出現了p[j] = p[ next[j] ]咋辦呢?如果出現了,則需要再次遞迴,即令next[j] = next[ next[j] ]。

優化策略:當出現p[j] = p[ next[j ]]時,再次遞迴,令next[j] = next[ next[j] ]

ps:當然優化後,仍然可能會出p[j] = p[ next[j ]]=p[ next[ next[j ] ]],我們可以再遞迴一次;當然滿足這種這種場景的出現是小概率事件,也就沒必要再遞迴一次了;

只要求出了原始next 陣列,便可以根據原始next 陣列快速求出優化後的next 陣列。還是以abab為例,如下**所示:

public

static

void

getnext

(string p,

int[

] next)

else

}else

}}

假設主串長度為n,模式串長度為m;

暴力法時間複雜度為o(m*n);

kmp演算法求next陣列時間複雜度為o(m),匹配過程時間複雜度為o(m),總的時間複雜度為o(m+n);

**:從頭到尾徹底理解kmp

KMP字串模式匹配

1.描述 指標i不回溯的字串模式匹配演算法 include include using namespace std 名稱 計算next陣列函式 描述 用於計算kmp模式匹配演算法的next陣列。該方法已得到修正,可以避免連續相同元素的問題 編寫人 李一帆 引數 patternstring 模式串 引...

字串匹配之kmp

kmp主要就是計算字首函式e q max return m void kmp char s,char p int n strlen s int m prefixcomp p,e int k 0 for int i 1 i n i putchar n 習題 試說明如何通過檢查字串pt的字首函式e,來確...

字串模式匹配KMP演算法

next的值去改變每次匹配的位置 注意 字串的儲存最好用字元陣列,然後用字元輸入的形式,保證正確!利用求模式串的next值來分析遍歷,可以在不改變主串i的值的基礎上,只改變next j 的下標來遍歷 next j include include using namespace std void ge...