RK指纹字符串查找算法

源代码:

import java.math.BigInteger;
import java.util.Random;

import edu.princeton.cs.algs4.StdOut;

public class RabinKarp {
    private String pat;    //模式字符串
    private long patHash;    //模式字符串散列值
    private int M;     //模式字符串的长度
    private long Q;    //很大的素数
    private int R;    //字母表的大小
    private long RM;    //R^(M-1) % Q

    public RabinKarp(char[] pat, int R){
        this.pat = String.valueOf(pat);
        this.R = R;
    }
    
    public RabinKarp(String pat){
        this.pat = pat;
        R = 256;
        M = pat.length();
        Q = longRandomPrime();
        
        RM = 1;
        for(int i=1; i<=M-1; i++){
            RM = (R * RM) % Q;
        }
        patHash = hash(pat, M);
    }
    
    private long hash(String str, int M){
        long h = 0;
        for(int i=0; i < M; i++){
            h = (R * h + str.charAt(i)) % Q;
        }
        return h;
    }
    
    public boolean check(String txt,int i){
        for(int j = 0; j < M; j++){
            if(pat.charAt(j) != txt.charAt(i+j))
            return false;
        }
        return true;
    }
    
    private static long longRandomPrime() {
        BigInteger prime = BigInteger.probablePrime(31, new Random());
        return prime.longValue();
    }
    
    private int search(String txt){
        int N = txt.length();
        if(N < M) return N;
        long txtHash = hash(txt,M);
        
        if((txtHash == patHash) && check(txt, 0)) return 0;
        for(int i = M; i < N; i++){
            txtHash = (txtHash + Q - RM*txt.charAt(i-M) % Q) % Q;
            txtHash = (txtHash*R + txt.charAt(i)) % Q;
            int offset = i-M+1;
            if((patHash == txtHash) && check(txt, offset))
                return offset;
        }
        return N;
    }
    
    public static void main(String[] args) {
        String pat = args[0];
        String txt = args[1];

        RabinKarp searcher = new RabinKarp(pat);
        int offset = searcher.search(txt);
        // print results
        StdOut.println("text:    " + txt);

        // from brute force search method 1
        StdOut.print("pattern: ");
        for (int i = 0; i < offset; i++)
            StdOut.print(" ");
        StdOut.println(pat);
    }
}

算法的关键思想在于文本字符每次移动一位,如何根据已知计算的hash值来推出移动一位的hash值。这其中涉及求模运算的基本性质。

(a + b) % p = (a % p + b % p) % p 
(a - b) % p = (a % p - b % p) % p 
(a * b) % p = (a % p * b % p) % p 
a ^ b % p = ((a % p)^b) % p 

猜你喜欢

转载自blog.csdn.net/weixin_42230824/article/details/86764505