alg4 子字串查詢 KMP演算法

2021-10-12 17:52:12 字數 3107 閱讀 6162

kmp演算法的基本思想是當出現不匹配時,就能知曉一部分文字內容(因為在匹配失敗之前它們已經和模式相匹配)。我們可以利用這些資訊避免將指標回退到所有這些已知的字元之前。

kmp演算法的主要思想是提前判斷如何重新開始查詢,而這種判斷只取決於模式本身。

在kmp子字串查詢演算法中,不會回退文字指標i,而是使用乙個陣列dfa來記錄匹配失敗時模式指標j應該回退多遠。對於每個字元c,在比較了c和pat.chatat(j)之後,dfa[c][j]表示的是應該和下個文字字元比較和模式字元的位置。在查詢中,dfa[txt.charat(i)][j]是在比較了txt.charat(i)和pat.charat(j)之後應該和txt.charat(i+1)比較的模式字元位置。在匹配時會繼續比較下乙個字元,因此dfa[pat.charat(j)][j]總是j+1。

當i和j所指向的字元匹配失敗時,模式可能匹配的下乙個位置應該從i-dfa[txt.charat(i)][j]處開始。從該位置開始的dfa[txt.charat(i)][j]個字元和模式的前dfa[txt.charat(i)][j]個字元應該相同,因此無需回退指標i,只需要將j設為dfa[txt.charat(i)][j]並將i加1即可,這正是當i和j所指向的字元匹配時的行為。

dfa陣列正是乙個確定優先狀態自動機(dfa)。如圖顯示的確定有限狀態自動機是由狀態(數字標記的圓圈)和轉換(帶標籤的箭頭)組成的。模式中的每個字元都對應著乙個狀態,每個此類狀態能夠轉換為字母表中的任意字元。

對於子字串查詢問題,這些轉換中只有一條是匹配轉換,其他都是非匹配轉換。所有狀態都和字元的比較相對應,每個狀態都表示乙個模式字串的索引值。當我們在標記為j的狀態中檢查文字中的第i個字元時,自動機的行為是這樣的:「沿著轉換dfa[txt.charat(i)][j]前進並繼續檢查下乙個字元(將i加1)」。對於乙個匹配的轉換,就向右移動一位,因為dfa[pat.charat(j)][j]的值總是j+1;對於乙個非匹配轉換,就向左移動。我們還包含了乙個不會進行任何轉換的停止狀態m。自動機從狀態0開始:如果自動機到達了狀態m,那麼就在文字中找到了和模式匹配的一段子字串;如果自動機在文字結束時都未能到達狀態m,那麼就可以知道文字中不存在匹配該模式的子字串。

每個模式字串都對應著乙個自動機(由儲存了所有轉換的dfa陣列表示)。

kmp演算法的關鍵問題:如何計算給定模式相對應的dfa陣列?

當在pat.charat(j)處匹配失敗時,希望了解的是,如果回退了文字指標並右移一位之後重新掃瞄已知的文字字元,dfa的狀態會是什麼?我們其實並不想回退,只是想將dfa重置到適當狀態,就好像已經回退過文字指標一樣。

這裡的關鍵在於需要重新掃瞄的文字字元正是pat.charat(1)到pat.charat(j-1)之間,忽略了首字母是因為模式需要右移一位,忽略了最後乙個字元是因為匹配失敗。這些模式中的字元都是已知的,因此對於每個可能匹配失敗的位置都可以預先找到重啟dfa的正確狀態,如圖所示。

dfa應該如何處理下乙個字元?和回退時的處理方式相同,除非在pat.charat(j)處匹配成功,這時dfa應該前進到狀態j+1。例如,對於ababac,要判斷在j=5時匹配失敗後dfa應該怎麼做。通過dfa可以知道完全回退之後演算法會掃瞄baba並達到狀態3,因此可以將dfa[3]複製到dfa[5]並將c所對應的元素的值設為6,因為pat.charat(5)是c(匹配)。因為在計算dfa的第j個狀態時只需要知道dfa是如何處理前j-1個字元的,所以總能從尚不完整的dfa中得到所需的資訊。

計算中最後乙個關鍵細節是,因為重啟位置x由上述討論可得以下**來構造給定模式的dfa:

dfa[pat.

charat(0

)][0

]=1;

for(

int x =

0, j =

1;j < m;j++

) dfa[pat.

charat

(j)]

[j]= j +1;

x = dfa[pat.

charat

(j)]

[x];

}

對於每個j,它將會:

構造軌跡:

對於長度為m的模式字串和長度為n的文字,kmp演算法訪問的字元不會超過m+n個。

構造dfa所需的時間和空間將與mr成正比(r為字母表大小)。

kmp演算法為最壞情況提供的線性級別執行時間保證是乙個重要的理論成果。在實際應用中,它比暴力演算法的速度優勢並不十分明顯,因為極少有應用程式需要在重複性很高的文字中查詢重複性很高的模式。但該方法的乙個優點是不需要在輸入中回退。這使得kmp子字串查詢演算法更適合在長度不確定的輸入流中進行查詢,需要回退的演算法在這種情況下則需要複雜的緩衝機制。

package section5_3;

public

class

kmp dfa[pat.

charat

(j)]

[j]= j +1;

//設定匹配成功情況下的值

x = dfa[pat.

charat

(j)]

[x];

//更新重啟狀態}}

public

intsearch

(string txt)

if(j == m)

return i - m;

//找到匹配(到達模式字串的結尾)

else

return n;

//未找到匹配(到達文字字串的結尾)

}public

static

void

main

(string[

] args)

system.out.

println

(pat);}

}

輸出:

字串查詢演算法kmp

字串查詢最簡單的方法就是乙個乙個地 滑動 查詢。這樣查詢演算法複雜度可定很高,假設pattern的長度為m,文字txt的長度為n,那麼演算法複雜度為o m n m 1 kmp模式搜尋演算法 kmp knuth morris pratt 我只認識knuth,大名鼎鼎的高納德老頭子嘛。kmp演算法的基本...

字串查詢演算法kmp

給定乙個文字串s,和乙個匹配串p,要求查詢p第一次在s中出現的位置。常見的方法如暴力搜素,逐個匹配s i p j 若匹配,下標後移。不匹配,i回溯到這次匹配前的下一位置,j置為0,重新匹配。最壞情況,時間複雜度o n m int violencesearch char s,char p else i...

字串查詢KMP演算法

如果你用過ctrl f這個快捷鍵,那麼你有很大的概率使用過這個演算法,這就是在待查詢字串 可能有成千上萬個字元 中找出模式串 比較小,可能有幾個字元 可能找到大於或者等於1次的位置。例如,在ababcd中找出abc。這裡介紹演算法思想,只給出了第一次出現的位置。void find char t,ch...