思路参照《算法导论》,请自行百度。
代码:
package stringMatch;
public class RKMethod {
int [] text={1,2,4,5,7,1,9,0,5,7,2,4,6,5};
int [] pattern={2,4,6};
int n=text.length; //text 长度
int m=pattern.length; //p长度
int p=0; //pattern 匹配
int t=0; //text匹配
int t1=0;
int h=1; //存放m-1次幂的进制
int d=10; //10进制
int q=13; //取余
public static void main(String[] args) {
RKMethod m = new RKMethod();
m.pre();
m.match();
}
public void pre(){
//先计算d的值
for(int i=0;i<m-1;i++){
h=h*d;
//System.out.println("h:"+h);
}
h=h%q;
for(int i=0;i<m;i++){
p = (d *p+pattern[i])%q;
t=(d*t+text[i])%q;
//p=p%q;
//t=t%q;
}
// System.out.println("P:"+p);
// System.out.println("t:"+t);
// System.out.println("------");
System.out.println("P:"+p);
System.out.println("t:"+t);
System.out.println("------");
}
public void match(){
//循环次数是否正确?
for(int i=0;i<n-m+3;i++){
if(p==t){
boolean istrue=true;
for(int j=0;j<m;j++){
if(pattern[j]!=text[i+j]){
istrue=false;
}
}
if(istrue){
System.out.println("匹配成功");
System.out.println("匹配位置:第"+(i+1)+"个");
return;
}else{
}
}
if(i<(n-m)){
t=(d*(t-text[i]*h)+text[i+m]) % q ;//计算下一位,T需要重新计算
if(t<0){
t=t+13;
}
// System.out.println("t:"+t);
}
}
System.out.println("匹配失败");
}
}
输出结果:
P:12
t:7
------
匹配成功
匹配位置:第11个
------------------------------------------------------
代码不是很规范,主要是讲思路。
因为朴素的字符匹配算法会进行许多无效的比较,因此在做字符匹配的优化时候只需对可能匹配的位置进行比较就可以了。
按照这个思路,需要先对 模板pattern 跟 匹配文本text(要在这里找到结果) 进行预处理。按照Rabin-Karp 字符匹配法的思路, 需要对pattern 跟 text 的前 m 位(也就是pattern的长度) 进行处理,思路是看成是一个多位数(例如例子中,为了便于说明,先规定好一个进制,这里选10进制,所包含的字符串为0,1,2....9):
pattern 看作是 246, text 前m 位看作是124 让他们对数字 13 取余。以上预处理便做好了。
预处理的时间复杂度:O(m)
核心的思路是降低匹配的次数,然后尽可能的选择大的偏移。由于可能存在伪命中点,这样的话匹配的时间就可以缩短了,再逐字地进行比较就可以判断是否能够真的匹配上了。
在偏移的过程中,在计算t 的值的时候,可以在前一个t 的基础上进行加工,没必要重新计算下一个t , 而是通过关系式:
t=(d*(t-text[i]*h)+text[i+m])
比如说是 1 ,2, 3, 4 匹配长度是3
那么在 从 123 再计算 234 时,只需要 对123 先变成23,再乘进制数 10 变成 230 然后在加上text 数组的下一位 4 ,变成 234 , 从而利用了前一个的信息节约了计算时间。
时间复杂度会在我学了时间复杂度计算再补上的。