字串匹配——Rabin–Karp algorithm(二)
上一篇https://blog.csdn.net/To_be_to_thought/article/details/84890018只是介紹了樸素的Rabin–Karp algorithm,這一篇主要說說樸素Rabin–Karp algorithm的優化。
模式串P長度為L,文字串S長度為n,在S的一輪遍歷中找到P的位置,上文提到的hash(P)的複雜度為O(L),對S的每個長度為L的子字串進行hash函式計算需要O(nL),如果某個子串雜湊值與hash(P)相等,則進行該子串與P的一一比對,該步複雜度為O(L),這種樸素的方法總共花費O(nL)的時間。
我們注意到這些字串的字元有很多是相互重合的,比如字串“algorithms”的5字元的子字串“algor”和“lgori”有四個字母是一樣的,如果能利用這個共享子串來減少計算,這就是“rolling hash”的由來。
舉個例子:P=“90210”,S=“48902107”
S的5字元的子串包括: ,數字字符集為 。
子字串k的雜湊函式計算方法:
遞推公式(類似於滑動視窗)為:
使用rolling hash將計算每個子串的雜湊值複雜度變成O(1),而所有子字串的雜湊值計算也降到了O(n)複雜度。
更一般的:
其中, 表示文字串裡的第i+1個長度為L的子字串, 為文字串中第i個字元(i從0到n-L取值)。
演算法程式碼如下:
class Solution {
public static int base=256;
public static int module=101;
public static boolean match(String str1,String str2)
{
assert str1.length()==str2.length();
for(int i=0;i<str1.length();i++)
{
if(str1.charAt(i)!=str2.charAt(i))
return false;
}
return true;
}
public int strStr(String haystack, String needle) {
if(needle=="" || needle.length()==0)
return 0;
int m=needle.length(),n=haystack.length(),h=1;
if(n==0 || n<m)
return -1;
for(int i=0;i<m-1;i++)
h=(h*base)%module;
int p=0,t=0;
for(int i=0;i<m;i++)
{
p=(p*base+needle.charAt(i))%module;
t=(t*base+haystack.charAt(i))%module;
}
for(int i=0;i<n-m+1;i++)
{
if(t==p)
{
if(match(needle,haystack.substring(i,i+m)))
return i;
}
if(i<n-m)
{
t=( base * (t-haystack.charAt(i) * h) + haystack.charAt(i+m) )%module;
if(t<0)
t=t+module;
}
}
return -1;
}
}