Return the number of distinct non-empty substrings of text that can be written as the concatenation of some string with itself (i.e. it can be written as a + a where a is some string).
Example 1:
Input: text = “abcabcabc”
Output: 3
Explanation: The 3 substrings are “abcabc”, “bcabca” and “cabcab”.
Example 2:
Input: text = “leetcodeleetcode”
Output: 2
Explanation: The 2 substrings are “ee” and “leetcodeleetcode”.
Constraints:
- 1 <= text.length <= 2000
- text has only lowercase English letters.
这题的关键是 Rabin-Karp 算法来找相邻的重复子字符串, 关于 Rabin-Karp 算法,大家自己去查吧,我也只是现学现用, 理解不多就不班门弄斧了。
use std::collections::HashSet;
impl Solution {
const M: i64 = 10i64.pow(9) + 7;
const P: i64 = 31;
fn hash(chars: &Vec<char>, len: usize) -> i32 {
let mut d = 1;
for _ in 1..len {
d *= Solution::P;
d %= Solution::M;
}
let mut ans = 0;
let mut left = Vec::new();
let mut right = Vec::new();
let mut left_hash = 0;
let mut right_hash = 0;
let mut chars = chars.clone();
let mut set = HashSet::new();
while !chars.is_empty() {
right.push(chars.remove(0));
if right.len() > len {
let c = right.remove(0);
right_hash += Solution::M;
right_hash -= d * c as i64 % Solution::M;
right_hash %= Solution::M;
left.push(c);
}
right_hash *= Solution::P;
right_hash %= Solution::M;
right_hash += *right.last().unwrap() as i64;
right_hash %= Solution::M;
if left.len() > len {
let c = left.remove(0);
left_hash += Solution::M;
left_hash -= d * c as i64 % Solution::M;
left_hash %= Solution::M;
}
if !left.is_empty() {
left_hash *= Solution::P;
left_hash %= Solution::M;
left_hash += *left.last().unwrap() as i64;
left_hash %= Solution::M;
}
if left_hash == right_hash {
if !set.contains(&left_hash) {
set.insert(left_hash);
ans += 1;
}
}
}
ans
}
pub fn distinct_echo_substrings(text: String) -> i32 {
let chars: Vec<char> = text.chars().collect();
let mut ans = 0;
for len in 1..=chars.len() / 2 {
ans += Solution::hash(&chars, len);
}
ans
}
}