该算法通过计算每个固定长度的字串,然后通过滚动hash依次对每个子串的hash值与匹配串进行比较,如果相同就打印子串所在的位置。由于使用的是hash值,可能产生hash冲突,可以在hash值相同以后在进行逐个字符匹配。
public class PabinKarp {
public static void main(String[] args) {
String s = "ABABABA";
String p = "ABA";
//方法开始
match(p,s);
}
private static void match(String p, String s) {
//计算匹配串的hash值
long hash_p = hash(p);
//计算第一个字串的hash值,并存入数组当中
long [] hashOfS = hash(s,p.length());
match(hash_p,hashOfS);
}
private static void match(long hash_p, long[] hash_s) {
//逐个对hash值进行比较,如果相同则打印第一个字符的位置
for (int i = 0; i < hash_s.length; i++) {
//这里可能会产生hash冲突,会导致匹配串和子串的值不一样,如需改进,可以在这进行再次字串的匹配
if (hash_s[i] == hash_p) {
System.out.println("match:"+i);
}
}
}
static int seed = 31;
static long hash(String str) {
long hash = 0;
for (int i = 0; i != str.length(); i++) {
hash = seed * hash + str.charAt(i);
}
return hash%Long.MAX_VALUE;
}
//滚动hash的核心方法
static long[] hash(final String s,final int n) {
long[] res = new long[s.length() - n +1];
res[0] = hash(s.substring(0,n));
for (int i = n; i < s.length() ; i++) {
char newChar = s.charAt(i);
char oldChar = s.charAt(i - n);
//将字符串转成31进制的hash值,进行滚动hash,可能导致结果很大
//所以进行取模,例如如果我们字符串为1234,分成两个字串123和234,逐个进行hash,就会得到123的hash值:
//1*31^2+2*31^+3*31^0=1026,如果我们要得到234的hash值,我们不用再次逐个进行hash我们只需要:
//(1*31^2+2*31^+3*31^0)*31-1*31^3+4
// = (1*31^3+2*31^2+3^31^1)-1*31^3+4
// =2*31^2+3^31^1+4*31^0
long v = (long)(res[i -n] * seed - Math.pow(seed,n) * oldChar + newChar)%Long.MAX_VALUE;
res[i-n+1] = v;
}
return res;
}
}