【问题标题】:Does Rust contain a way to directly check whether or not one vector is a "substring" of another?Rust 是否包含一种直接检查一个向量是否是另一个向量的“子字符串”的方法?
【发布时间】:2017-10-31 19:25:50
【问题描述】:

您可以使用 contains 搜索模式,但 Vec::contains 用于单个元素。

我能够做到这一点的唯一方法是直接实现某种子字符串函数,但我有点希望有一个内置的方法。

let vec1 = vec![1, 2, 3, 4, 5];
let vec2 = vec![2, 3]; // vec2 IS a substring of vec1
let vec3 = vec![1, 5]; // vec3 is NOT a substring of vec3

fn is_subvec(mainvec: &Vec<i32>, subvec: &Vec<i32>) -> bool {
    if subvec.len() == 0 { return true; }
    if mainvec.len() == 0 { return false; }

    'outer: for i in 0..mainvec.len() {
        for j in 0..subvec.len() {
            if mainvec[i+j] != subvec[j] {
                continue 'outer;
            }
        }
        return true;
    }
    return false;
}

println!("should be true: {}", is_subvec(&vec1, &vec2));
println!("should be false: {}", is_subvec(&vec1, &vec3));

我见过How can I find a subsequence in a &[u8] slice?,但那是专门针对u8 的,我想要一些适用于Vec 中的类型的东西。

【问题讨论】:

标签: vector rust


【解决方案1】:

Rust 不包含在标准库中。

一般来说,这是我们可以在任意字母上定义的子字符串搜索问题。根据我们可用的属性(仅可比较的或可订购的)决定我们可以使用哪些算法。

使用子字符串搜索算法的好处是该函数对所有输入都执行得相当好。蛮力搜索解决方案有一个最坏情况,它需要的时间是输入大小的二次方。

i32 值的“字母表”是orderable,因此 TwoWay 算法(Rust 标准库在 str::find(&amp;str) 内部使用)可以适应实现这一点。

一种适用于所有平等可比字母的算法是Knuth-Morris-Pratt。它需要对我们正在搜索的模式进行预处理,并且需要与模式长度成比例的空间。实现起来也很简单。

我已经为 Rust @bluss/knuth-morris-pratt 编写了通用元素算法的实现,至少在撰写本文时,它还没有作为 crate 发布。


好吧,天哪。你可能已经把我当成了书呆子。为此,我花费了大量时间研究使用不超过T: Eq不超过常量空间(意味着Rust 核心兼容)的算法。在撰写本文时,您可以使用它:galil-seiferas

【讨论】:

    【解决方案2】:

    据我所知,标准库中没有函数或方法来检查一个切片是否是另一个切片的子切片。

    我们可以将您的算法推广和简化为 (playground):

    fn is_sub<T: PartialEq>(mut haystack: &[T], needle: &[T]) -> bool {
        if needle.len() == 0 { return true; }
        while !haystack.is_empty() {
            if haystack.starts_with(needle) { return true; }
            haystack = &haystack[1..];
        }
        false
    }
    
    fn main() {
        let vec1 = vec![1, 2, 3, 4, 5];
        let vec2 = vec![2, 3]; // vec2 IS a substring of vec1
        let vec3 = vec![1, 5]; // vec3 is NOT a substring of vec3
    
        println!("should be true: {}", is_sub(&vec1, &vec2));
        println!("should be false: {}", is_sub(&vec1, &vec3));
    }
    

    逻辑是我们检查needle切片是否是haystack切片的前缀,如果不是,我们从haystack中“删除”一个元素。如果haystack 最终为空,则它不是子切片。

    windows 的帮助下,我们可以通过函数式编程的方法进一步缩短is_sub

    fn is_sub<T: PartialEq>(haystack: &[T], needle: &[T]) -> bool {
        haystack.windows(needle.len()).any(|c| c == needle)
    }
    

    值得注意的是,这些算法并不是最快的算法,因为它们的最坏情况复杂度为O(n^2),但它们适用于所有T: PartialEq。可以使用 Knuth-Morris-Pratt 等算法来加快这一速度,但对于较小的输入,O(n^2) 可能会因为常数因素而更好。

    我们可以通过结合专业化(需要每晚)使用特质来改善这种情况:

    #![feature(specialization)]
    
    pub trait HasPart {
        fn has_part(&self, rhs: &Self) -> bool;
    }
    
    impl<T: PartialEq> HasPart for [T] {
        default fn has_part(&self, rhs: &Self) -> bool {
            self.windows(rhs.len()).any(|curr| curr == rhs)
        }
    }
    
    impl HasPart for [u8] {
        fn has_part(&self, rhs: &Self) -> bool {
            unimplemented!() // use Knuth-Morris-Pratt
        }
    }
    
    fn main() {
        let vec1 = vec![1, 2, 3, 4, 5];
        let vec2 = vec![2, 3]; // vec2 IS a substring of vec1
        let vec3 = vec![1, 5]; // vec3 is NOT a substring of vec3
    
        println!("should be true: {}", vec1.has_part(&vec2));
        println!("should be false: {}", vec1.has_part(&vec3));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-24
      • 1970-01-01
      • 2013-03-13
      • 2017-04-07
      • 2023-03-31
      • 1970-01-01
      相关资源
      最近更新 更多