【发布时间】:2021-11-30 15:39:17
【问题描述】:
我在 leetcode 中做练习,作为学习 Rust 的一种方式。一个练习涉及找到最长的子字符串,而字符串中没有任何字符重复。
我的第一个想法是将子字符串存储在字符串中并搜索字符串以查看字符是否已在其中:
impl Solution {
pub fn length_of_longest_substring(s: String) -> i32 {
let mut unique_str = String::from("");
let mut schars: Vec<char> = s.chars().collect();
let mut longest = 0 as i32;
for x in 0..schars.len()
{
unique_str = schars[x].to_string();
for y in x+1..schars.len()
{
if is_new_char(&unique_str, schars[y])
{
unique_str.push(schars[y]);
} else {
break;
}
}
let cur_len = unique_str.len() as i32;
if cur_len > longest {
longest = cur_len;
}
}
longest
}
}
fn is_new_char ( unique_str: &str, c: char ) -> bool {
if unique_str.find(c) == None
{
true
} else {
false
}
}
它工作正常,但性能偏低。希望在“查找”操作上减少几毫秒,我将 unique_str 替换为 HashMap:
use std::collections::HashMap;
impl Solution {
pub fn length_of_longest_substring(s: String) -> i32 {
let mut hash_str = HashMap::new();
let mut schars: Vec<char> = s.chars().collect();
let mut longest = 0 as i32;
for x in 0..schars.len()
{
hash_str.insert(schars[x], x);
for y in x+1..schars.len()
{
if hash_str.contains_key(&schars[y]){
break;
} else {
hash_str.insert(schars[y], y);
}
}
let cur_len = hash_str.len() as i32;
if cur_len > longest {
longest = cur_len;
}
hash_str.clear();
}
longest
}
}
令人惊讶的是,String.find() 版本在基准测试中比 HashMap 快 3 倍,尽管我使用的是相同的算法(或者至少我是这么认为的)。直觉上,我会假设在 hashmap 中进行查找应该比搜索字符串的字符要快得多,但结果恰恰相反。
有人能解释一下为什么 HashMap 这么慢吗? (或指出我做错了什么)。
【问题讨论】:
-
你的琴弦有多大?
-
不是 O(n) 可以解决的问题吗?
-
O(1) 算法(散列查找)可能比 O(n) 算法(线性搜索)慢,但对实际数据没有任何特殊了解,可能有多种原因,这主要是猜测。此实现存在一些问题,例如每个 hashmap 条目包含与键和值相同的内容,但这可能无关紧要。
HashMap默认使用慢速(加密)散列器;您可以通过将其换成快速的来做得更好。 -
请注意,问题不仅与输入字符串中的字符数有关,还与输入流中的 distinct 字符数有关,所以如果 leetcode 有一堆
ababaabacabbcaabccababc形式的测试无论是 map 还是Vec都不会比 n=3 长,这是很小的。但这又只是猜测。 -
另一件事是 rust 使用了一个相对较慢但哈希 dos 抗性的哈希函数。使用针对小型输入(例如 fnv)优化的哈希函数应该会快得多。
标签: performance rust hashmap