Find Substrings with Rabin-Karp Hashing
Search text for every occurrence of a pattern with a rolling Rabin-Karp hash and verify collisions by direct comparison.
Code
fn find_all(haystack: &str, needle: &str) -> Vec<usize> {
let hay: Vec<char> = haystack.chars().collect();
let pattern: Vec<char> = needle.chars().collect();
if pattern.is_empty() || pattern.len() > hay.len() {
return Vec::new();
}
const BASE: u64 = 257;
let mut highest = 1u64;
for _ in 1..pattern.len() {
highest = highest.wrapping_mul(BASE);
}
let mut pattern_hash = 0u64;
let mut window_hash = 0u64;
for index in 0..pattern.len() {
pattern_hash = pattern_hash
.wrapping_mul(BASE)
.wrapping_add(pattern[index] as u64);
window_hash = window_hash
.wrapping_mul(BASE)
.wrapping_add(hay[index] as u64);
}
let mut matches = Vec::new();
for start in 0..=hay.len() - pattern.len() {
if window_hash == pattern_hash && hay[start..start + pattern.len()] == pattern[..] {
matches.push(start);
}
if start + pattern.len() < hay.len() {
window_hash = window_hash
.wrapping_sub((hay[start] as u64).wrapping_mul(highest));
window_hash = window_hash
.wrapping_mul(BASE)
.wrapping_add(hay[start + pattern.len()] as u64);
}
}
matches
}
fn main() {
assert_eq!(find_all("abracadabra", "abra"), vec![0, 7]);
assert_eq!(find_all("aaaa", "aa"), vec![0, 1, 2]);
assert_eq!(find_all("short", "longer pattern"), Vec::<usize>::new());
assert_eq!(find_all("anything", ""), Vec::<usize>::new());
println!("{:?}", find_all("abracadabra", "abra"));
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.