Rabin Karp演算法學習

2021-06-22 04:06:27 字數 4758 閱讀 1044

rabin-karp 演算法(字串快速查詢)

傳統的字串搜尋演算法時間複雜度為o(nm),其中n=|t|,m=|p|。具體**如下:
int findmatch(char *p, char *t)

return (-1);

}

rabin-karp 演算法是基於這樣的思路:即把字串看作是字符集長度進製的數,由數值的比較結果得出字串的比較結果。

樸素的字串匹配演算法為什麼慢?因為它太健忘了,前一次匹配的資訊其實有部分可以應用到後一次匹配中去,而樸素的字串匹配演算法只是簡單的把這個資訊扔掉,從頭再來,因此,浪費了時間。好好的利用這些資訊,自然可以提高執行速度。

由於完成兩個字串的比較需要對其中包含的字元進行逐個比較,所需的時間較長,而數值比較則一次就可以完成,那麼我們首先把「搜尋詞」中各個字元的「碼點值」通過計算,得出乙個數值(這個數值必須可以表示出字元的前後順序,而且可以隨時去掉某個字元的值,可以隨時新增乙個新字元的值),然後對「源串」中要比較的部分進行計算,也得出乙個數值,對這兩個數值進行比較,就能判斷字串是否匹配。對兩個數值進行比較,速度比簡單的字串比較快很多。

比如我們要在源串

"9876543210520"

中查詢

"520"

,因為這些字串中只有數字,所以我們可以使用字符集 來表示字串中的所有元素,並且將各個字元對映到數字 0~

9,然後用 m 表示字符集中字元的總個數,這裡是

10,那麼我們就可以將搜尋詞

"520"

轉化為下面的數值:

("5"

的對映值 * m +

"2"的對映值) * m +

"0"的對映值 = (

5 *

10 +

2) *

10 +

0 =

520  當然,如果「搜尋詞」很長,那麼計算出來的這個數值就會很大,這時我們可以選乙個較大的素數對其取模,用取模後的值作為「搜尋詞」的值。

分析一下這個數值:

520,它可以代表字串

"520"

,其中:

代表字元

"5" 的部分是「

"5"的對映值 * (m 的 n -

1 次方) =

5 * (

10 的

2 次方) =

500」

代表字元

"2" 的部分是「

"2"的對映值 * (m 的 n -

2 次方) =

2 * (

10 的

1 次方) = 20」

代表字元

"0" 的部分是「

"0"的對映值 * (m 的 n -

3 次方) =

0 * (

10 的

0 次方) = 0」

(n 代表字串的長度)

我們可以隨時減去其中乙個字元的值,也可以隨時新增乙個字元的值。

「搜尋詞」計算好了,那麼接下來計算「源串」,取「源串」的前 n 個字元(n 為「搜尋詞」的長度)

"987"

,按照同樣的方法計算其數值:

("9"

的對映值 * m +

"8"的對映值) * m +

"7"的對映值 = (

9 *

10 +

8) *

10 +

7 =

987  然後將該值與搜尋詞的值進行比較即可。

比較發現

520 與

987 不相等,則說明

"520"

與 "987"

不匹配,則繼續向下尋找,這時候該如何做呢?下一步應該比較

"520"

跟 "876"

了,那麼我們如何利用前一步的資訊呢?首先我們把

987 減去代表字元

"9" 的部分:

987 - (

"9"的對映值 * (m 的 n -

1 次方)) =

987 - (

9 * (

10 的

2 次方)) =

987 -

900 =

87  然後再乘以 m(這裡是

10),再加上

"6" 的對映值,不就成了

876 了麼:

87 * m +

"6"的對映值 =

87 *

10 +

6 =

876  當然了,由於採用了取模操作,當兩個數值相等時,未必是真正的相等,我們需要進行一次細緻的檢查(再進行一次樸素的字串比較)。若不匹配,則可以排除掉。繼續下一步。

如果我們要在 ascii 字符集範圍內查詢「搜尋詞」,由於 ascii 字符集中有

128 個字元,那麼 m 就等於

128,比如我們要在字串

"abcdefg"

中查詢

"cde"

,那麼我們就可以將搜尋詞

"cde"

轉化為「(

"c"的碼點 * m +

"d"的碼點) * m +

"e"的碼點 = (

99 *

128 +

100) *

128 +

101 =

1634917

」這樣乙個數值。

分析一下這個數值:

1634917

,它可以代表字串

"cde"

,其中:

代表字元

"c" 的部分是「

"c"的碼點 * (m 的 n -

1 次方) =

99 * (

128 的

2 次方) =

1622016

」代表字元

"d" 的部分是「

"d"的碼點 * (m 的 n -

2 次方) =

100 * (

128 的

1 次方) =

12800

」代表字元

"e" 的部分是「

"e"的碼點 * (m 的 n -

3 次方) =

101 * (

128 的

0 次方) =

101」

(n 代表字串的長度)

我們可以隨時減去其中乙個字元的值,也可以隨時新增乙個字元的值。

「搜尋詞」計算好了,那麼接下來計算「源串」,取「源串」的前 n 個字元(n 為「搜尋詞」的長度)

"abc"

,按照同樣的方法計算其數值:

("a"

的碼點 * m +

"b"的碼點) * m +

"c"的碼點 = (

97 *

128 +

98) *

128 +

99 =

1601891

然後將該值與「搜尋詞」的值進行比較即可。

比較發現

1634917

與 1601891

不相等,則說明

"cde"

與 "abc"

不匹配,則繼續向下尋找,下一步應該比較

"cde"

跟 "bcd"

了,那麼我們如何利用前一步的資訊呢?首先去掉

"abc"

的數值中代表 a 的部分:

(1601891

- "a"

的碼點 * (m 的 n -

1 次方)) = (

1601891

- 97

* (128 的

2 次方)) =

12643

然後再將結果乘以 m(這裡是

128),再加上

"d" 的碼點值不就成了

"bcd"

的值了嗎:

12643

* 128

+ "d"

的碼點 =

1618304

+ 100

= 1618404

這樣就可以繼續比較

"cde"

和 "bcd"

是否匹配,以此類推。

如果我們要在 unicode 字符集範圍內查詢「搜尋詞」,由於 unicode 字符集中有

1114112

個字元,那麼 m 就等於

1114112

,而 go 語言中使用

16777619

作為 m 的值,

16777619

比 1114112

大(更大的 m 值可以容納更多的字元,這是可以的),而且

16777619

是乙個素數。這樣就可以使用上面的方法計算 unicode 字串的數值了。進而可以對 unicode 字串進行比較了。

其實 m 可以理解為進製值,比如

10 進製就是 10,

128 進製就是

128,

16777619

進製就是

16777619

轉於

字串匹配演算法 Rabin Karp 演算法

字串匹配演算法 rabin karp 演算法 參考資料 1 演算法導論 2 lalor 3 記憶碎片 在實際應用中,rabin karp 演算法對字串匹配問題能較好的執行。rabin karp 演算法需要對字串和模式進行預處理,其預處理時間為 o m 在最壞情況下的執行時間為 o n m 1 m 但...

演算法學習 Union Find演算法

union find演算法有它的實際用途。多用於動態連通的應用場景。union find演算法是 給出兩個節點,判斷它們是否連通,如果連通,是不需要給出具體的路徑的 舉兩個例子作為主要表現 1 在網路連線中,當發現沒有連線的兩個節點,可以把他們連線起來,一旦節點都連線起來,又能把多餘的線拆除,這時候...

演算法學習 KM演算法

km演算法 用於求二分圖的最佳完美匹配 即權值最大的完美匹配 如果你也是個剛來學習km演算法的人 大概的用途肯定還是知道的吧 還是直接說重點吧 首先 理解km演算法前 必須有以下3個概念 1.可行頂標 對於乙個賦值二分圖g x,y,e,w x,y 代表二分圖的兩邊頂點標號 e代表邊 w代表邊的權值 ...