按照题意, 很容易写入如下枚举的方式, 时间复杂度 \(O(n^3 + c \times n^2)\)
枚举边界 \(n^2\), 内层统计个数 \(n + C\)
最后超时
pub fn unique_letter_string(s: String) -> i32 {
fn count_unique_chars(s: &str) -> usize {
use std::collections::HashMap;
let mut counter = HashMap::new();
for chr in s.chars() {
*counter.entry(chr).or_insert(0) += 1;
}
counter.into_iter().filter(|(_, v)| *v == 1).count()
}
let mut cnt = 0;
for i in 0..s.len() {
for j in i..s.len() {
cnt += count_unique_chars(s.get(i..=j).unwrap());
}
}
cnt as i32
}
优化方向: 将内层的遍历换成类似"滑动窗口", 不做重复统计
时间复杂度 \(O(n^2 + c \times n^2)\), 仍然超时
pub fn unique_letter_string(s: String) -> i32 {
use std::collections::HashMap;
let s = s.as_bytes();
let mut cnt = 0;
for i in 0..s.len() {
let mut counter = HashMap::new();
for j in i..s.len() {
{
*counter.entry(s[j]).or_insert(0) += 1;
}
cnt += counter.iter().filter(|(_, &v)| v == 1).count();
}
}
cnt as i32
}
其他思路:
对于字串 BCADEF
, 假定其前后都是A
, 即(A)BCADEF(A)
, 含统计字符A
的唯一串有
BCA, BCAD, BCADE, BCADEF, CA, CAD, CADE, CADEF, A, AD, ADE, ADEF
BC
的角度看, 后面有4种选择, (), D, DE, DEF
DEF
的角度看, 前面有3种选择, BC, C, ()
所以这个段内, 含统计A
字符的唯一串有 3 * 4 = 12, 即 (2+1) * (3+1),
2为当前A到前一个A之间字符个数, 3为当前A到后一个A之间字符个数
基于此, 可以对任意一个(A)..A..(A)
进行计算, 最终加和即为结果
边界情况: 字符只出现一次
时间复杂度\(O(n)\)
pub fn unique_letter_string(s: String) -> i32 {
let s = s.as_bytes();
let mut last_pos = vec![-1; 26];
let mut curr_pos = vec![-1; 26];
let mut ans = 0;
for (pos, &b) in s.iter().enumerate() {
let i = (b - b'A') as usize;
let pos = pos as i32;
if curr_pos[i] > -1 {
// 这个字符之前出现过, 这时的pos对应上述推导中的后一个
ans = ans + (pos - curr_pos[i]) * (curr_pos[i] - last_pos[i]);
}
last_pos[i] = curr_pos[i];
curr_pos[i] = pos;
}
// 对于只出现过一次的字符, 上面循环统计不到, 即等效 后一个 的位置为字符结尾
for (last, curr) in last_pos.into_iter().zip(curr_pos.into_iter()) {
if curr > -1 {
ans = ans + (curr - last) * (s.len() as i32 - curr);
}
}
ans
}