const R: usize = 256;
pub struct RabinKarp {
pat: String,
pat_hash: usize,
m: usize,
q: usize,
rm: usize,
}
impl RabinKarp {
pub fn new(pat: String) -> Self {
let m = pat.len();
let q = long_prandom_prim();
let mut rm = 1;
for _ in 1..m {
rm = (R * rm) % q;
}
let pat_hash = hash(q, &pat, m);
RabinKarp {
pat,
pat_hash,
m,
q,
rm,
}
}
pub fn search(&self, txt: &str) -> usize {
let n = txt.len();
if n < self.pat.len() {
return txt.len();
}
let mut txt_hash = hash(self.q, txt, self.m);
if self.pat_hash == txt_hash && self.check(txt, 0) {
0
} else {
for i in self.m..n {
txt_hash =
(txt_hash + self.q - self.rm * char_at(txt, i - self.m) % self.q) % self.q;
txt_hash = (txt_hash * R + char_at(txt, i)) % self.q;
let t = i - self.m + 1;
if self.pat_hash == txt_hash && self.check(txt, t) {
return t;
}
}
n
}
}
fn check(&self, txt: &str, start: usize) -> bool {
(0..self.pat.len()).all(|i| -> bool { char_at(txt, i + start) == char_at(&self.pat, i) })
}
}
fn char_at(txt: &str, i: usize) -> usize {
txt.as_bytes()[i] as usize
}
fn hash(q: usize, pat: &str, m: usize) -> usize {
let mut h = 0;
for j in 0..m {
h = (R * h + char_at(pat, j)) % q;
}
h
}
fn long_prandom_prim() -> usize {
100000073
}
#[cfg(test)]
mod test {
use crate::search::rabin_karp::RabinKarp;
#[test]
fn test() {
let kmp = RabinKarp::new("abc".to_string());
assert_eq!(kmp.search("ababac"), 6);
assert_eq!(kmp.search("ababcc"), 2);
assert_eq!(kmp.search("ababccababcc"), 2);
assert_eq!(kmp.search("abcbcc"), 0);
assert_eq!(kmp.search("babcbcc"), 1);
assert_eq!(kmp.search("aabcbcc"), 1);
assert_eq!(kmp.search("ab"), 2);
}
}