一、定義
Rabin-Karp算法,是由M.O.Rabin和R.A.Karp發(fā)明的一種基于散列的字符串查找算法。
通常情況下,基于散列的字符串查找步驟是:
- 首先計算模式字符串的散列函數(shù);
- 然后利用相同的散列函數(shù)計算文本中所有可能的M個字符的子字符串的散列函數(shù)值并尋找匹配
但是這種方法比暴力查找還慢,因為計算散列值會涉及字符串中的每個字符。Rabin和Karp對上述方法進(jìn)行了改進(jìn),發(fā)明了一種能夠在常數(shù)時間內(nèi)算出M個字符的子字符串散列值的方法。
二、基本思想
以文本“3141592653589793”,模式串“26535”為例。
比較思路如下:
尋找一個大素數(shù)(作為散列表的大小),通過“除留余數(shù)法”計算模式串的散列值。然后依次計算文本中的相同長度子串的散列值,進(jìn)行比較。
遞推文本串的散列值:
以Ti表示文本字符T[i],Xi表示文本串T[i...M-1-i]的整數(shù)值,其中M為模式串長度,則:
遞推可以得到:
我們可以在初始時求得字符串T[i...M-1-i]的hash值,即Xi%P = hash(txt, 0, M-1)(其中P為大素數(shù));
然后通過上述公式遞推就可以得到字符串T[i+1...M-i]的hash值,即Xi+1 % P。
三、實現(xiàn)
RK算法完整源碼:
public int search(String txt, String ptn) {
int N = txt.length();
int M = ptn.length();
if (M > N)
return -1;
long txtHash = hash(txt, 0, M - 1); // 計算文本串txt[0...M-1]的hash值
long ptnHash = hash(ptn, 0, M - 1); // 計算模式串的hash值
// 首先做一次匹配
if (ptnHash == txtHash)
return 0;
// 計算R^(M-1)%P,后續(xù)公式遞推求值用到
long RM = 1;
for (int i = 1; i <= M - 1; i++) {
RM = (RM * R) % P;
}
// 從文本的第1個字符開始查找
for (int i = 1; i <= N - M; i++) {
// 根據(jù)遞推公式,計算文本串hash值
txtHash = (txtHash + P - RM * txt.charAt(i) % P) % P;
txtHash = (txtHash * R + txt.charAt(i + M)) % P;
if (txtHash == ptnHash)
return i;
}
return -1;
}
四、性能分析
Rabin-Karp算法,由于通過計算模式串和文本子串的散列值來做相等性比較,所以有一定概率出現(xiàn)沖突,即散列值相同但是字符串不匹配。
出現(xiàn)沖突的概率與大素數(shù)的選擇有關(guān),概率約為1/Q(Q為大素數(shù)的值),實際應(yīng)用中,該算法是可靠的,只有極小的概率會出現(xiàn)沖突。
- 時間復(fù)雜度
O(N)