您可以使用String
使用搜索模式的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
中的类型如何。
答案 0 :(得分:5)
Rust并没有在标准库中包含它。
通常,这是我们可以在任意字母表上定义的子字符串搜索问题。根据我们可用的属性(仅可比较或可订购)确定我们可以使用的算法。
使用子字符串搜索算法的好处是该函数对所有输入都表现得相当好。强力搜索解决方案有一个最坏情况,它需要的时间是输入大小的二次方。
i32
值的“字母”是 orderable ,因此双向算法(Rust标准库在str::find(&str)
内部使用)可以适应实现这一点。
适用于所有相等可比字母的算法是 Knuth-Morris-Pratt 。它需要预处理我们正在搜索的模式,并且需要与模式长度成比例的空间。实施起来也很简单。
我已经为Rust @ bluss/knuth-morris-pratt编写了通用元素算法的实现,至少在撰写本文时,它并未作为包装箱发布。
嗯,基督。你可能有书呆子 - 非常努力地狙击我。我花费了大量时间研究算法,使用不超过T: Eq
和不超过常量空间(意味着Rust核心兼容)。在撰写本文时,它可以使用:galil-seiferas
。
答案 1 :(得分:3)
据我所知,标准库中没有函数或方法来检查切片是否是另一个切片的子切片。
我们可以将您的算法概括并简化为(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));
}