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代表邊的權值 ...