Rust练习册之字母异位词与字符串处理方法技巧

 更新时间:2026年02月04日 08:17:19   作者:一缕清烟在人间  
Rust作为一种系统编程语言,其在字符串拼接方面的设计既灵活又高效,这篇文章主要介绍了Rust练习册之字母异位词与字符串处理方法技巧的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在日常生活中,我们经常会遇到一些单词,它们由相同的字母组成,但顺序不同,这种词被称为"字母异位词"(Anagram)。比如 “listen” 和 “silent” 就是一对字母异位词。在 Exercism 的 “anagram” 练习中,我们将实现一个字母异位词查找器,这不仅能帮助我们理解字符串处理的基本技巧,还能深入学习 Rust 中的集合操作和字符处理。

问题背景

字母异位词是指由相同字母重新排列组成的不同单词。判断两个单词是否为字母异位词的核心思想是:如果两个单词包含完全相同的字母,且每个字母出现的次数也相同,那么它们就是字母异位词。

让我们先看看练习提供的实现:

use std::collections::HashSet;

fn sort(word: &str) -> String {
    let mut chars: Vec<char> = word.chars().collect();
    chars.sort_unstable();
    chars.into_iter().collect()
}

pub fn anagrams_for<'a>(word: &'a str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> {
    let word = word.to_lowercase();
    let sorted = sort(&word);
    possible_anagrams
        .iter()
        .filter(|e| {
            let x = e.to_lowercase();
            x != word && sorted == sort(&x)
        })
        .cloned()
        .collect()
}

这个实现采用了非常优雅的方法:将单词中的字符排序,如果两个单词排序后相同,那么它们就是字母异位词。

算法解析

1. 字符排序方法

fn sort(word: &str) -> String {
    let mut chars: Vec<char> = word.chars().collect();
    chars.sort_unstable();
    chars.into_iter().collect()
}

这个函数是整个算法的核心:

  1. 将字符串转换为字符向量
  2. 对字符进行排序
  3. 将排序后的字符重新组合成字符串

使用 sort_unstable 而不是 sort 是因为不需要稳定排序,这样可以获得更好的性能。

2. 主要逻辑

pub fn anagrams_for<'a>(word: &'a str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> {
    let word = word.to_lowercase();
    let sorted = sort(&word);
    possible_anagrams
        .iter()
        .filter(|e| {
            let x = e.to_lowercase();
            x != word && sorted == sort(&x)
        })
        .cloned()
        .collect()
}

主函数的逻辑非常清晰:

  1. 将目标单词转为小写并排序作为基准
  2. 遍历所有候选词
  3. 过滤条件:
    • 候选词不能与目标词相同(即使大小写不同)
    • 候选词排序后必须与目标词排序后相同
  4. 收集结果到 HashSet 中

测试用例分析

通过查看测试用例,我们可以更好地理解需求:

#[test]
fn test_no_matches() {
    let word = "diaper";

    let inputs = ["hello", "world", "zombies", "pants"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

最基本的情况,没有任何匹配的字母异位词。

#[test]
fn test_detect_simple_anagram() {
    let word = "ant";

    let inputs = ["tan", "stand", "at"];

    let outputs = vec!["tan"];

    process_anagram_case(word, &inputs, &outputs);
}

简单情况,“ant” 和 “tan” 是字母异位词。

#[test]
fn test_case_insensitive_anagrams() {
    let word = "Orchestra";

    let inputs = ["cashregister", "Carthorse", "radishes"];

    let outputs = vec!["Carthorse"];

    process_anagram_case(word, &inputs, &outputs);
}

大小写不敏感的匹配,“Orchestra” 和 “Carthorse” 是字母异位词。

#[test]
fn test_does_not_detect_a_word_as_its_own_anagram() {
    let word = "banana";

    let inputs = ["banana"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

一个词不能是它自己的字母异位词,即使大小写不同。

#[test]
fn test_unicode_anagrams() {
    let word = "ΑΒΓ";

    // These words don't make sense, they're just greek letters cobbled together.
    let inputs = ["ΒΓΑ", "ΒΓΔ", "γβα"];

    let outputs = vec!["ΒΓΑ", "γβα"];

    process_anagram_case(word, &inputs, &outputs);
}

支持 Unicode 字符,包括希腊字母。

替代实现方法

除了字符排序方法,还有其他几种判断字母异位词的方式:

1. 字符计数方法

use std::collections::HashMap;
use std::collections::HashSet;

fn char_count(word: &str) -> HashMap<char, usize> {
    let mut counts = HashMap::new();
    for c in word.chars() {
        *counts.entry(c).or_insert(0) += 1;
    }
    counts
}

pub fn anagrams_for<'a>(word: &'a str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> {
    let word = word.to_lowercase();
    let word_counts = char_count(&word);
    
    possible_anagrams
        .iter()
        .filter(|&candidate| {
            let candidate_lower = candidate.to_lowercase();
            candidate_lower != word && char_count(&candidate_lower) == word_counts
        })
        .cloned()
        .collect()
}

这种方法通过统计每个字符出现的次数来判断是否为字母异位词。

2. 排序优化版本

use std::collections::HashSet;

fn normalize(word: &str) -> String {
    let mut chars: Vec<char> = word.to_lowercase().chars().collect();
    chars.sort_unstable();
    chars.into_iter().collect()
}

pub fn anagrams_for<'a>(word: &'a str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> {
    let normalized_target = normalize(word);
    let target_lower = word.to_lowercase();
    
    possible_anagrams
        .iter()
        .filter(|&&candidate| {
            let candidate_lower = candidate.to_lowercase();
            candidate_lower != target_lower && normalize(candidate) == normalized_target
        })
        .cloned()
        .collect()
}

这个版本在 normalize 函数中就进行了小写转换,避免了重复转换。

性能比较

让我们分析一下不同方法的性能特点:

  1. 字符排序方法

    • 时间复杂度:O(n log n),其中 n 是单词长度
    • 空间复杂度:O(n)
    • 优点:实现简单,易于理解
    • 缺点:排序操作相对较慢
  2. 字符计数方法

    • 时间复杂度:O(n),其中 n 是单词长度
    • 空间复杂度:O(k),其中 k 是不同字符的数量
    • 优点:时间复杂度更优
    • 缺点:需要额外的 HashMap 存储

对于大多数实际应用,字符排序方法已经足够快,而且代码更简洁。

边界情况处理

在实现中需要特别注意以下边界情况:

#[test]
fn test_misleading_unicode_anagrams() {
    // Despite what a human might think these words different letters, the input uses Greek A and B
    // while the list of potential anagrams uses Latin A and B.
    let word = "ΑΒΓ";  // 希腊字母

    let inputs = ["ABΓ"];  // 拉丁字母 + 希腊字母

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

Unicode 字符的处理需要特别小心,因为看起来相似的字符可能有不同的编码。

#[test]
fn test_same_bytes_different_chars() {
    let word = "a⬂"; // 61 E2 AC 82

    let inputs = ["€a"]; // E2 82 AC 61

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

即使字节相同但字符顺序不同也不能算作字母异位词。

实际应用场景

字母异位词在实际开发中有多种应用:

  1. 文本处理工具:查找文档中的字母异位词
  2. 游戏开发:拼字游戏中的单词匹配
  3. 教育软件:语言学习应用中的练习题
  4. 数据清洗:识别重复但拼写不同的数据项

扩展功能

基于这个基础实现,我们可以添加更多功能:

use std::collections::HashSet;

pub struct AnagramSolver {
    word: String,
    sorted_chars: String,
}

impl AnagramSolver {
    pub fn new(word: &str) -> Self {
        let word = word.to_lowercase();
        let sorted_chars = Self::sort_chars(&word);
        AnagramSolver { word, sorted_chars }
    }
    
    fn sort_chars(word: &str) -> String {
        let mut chars: Vec<char> = word.chars().collect();
        chars.sort_unstable();
        chars.into_iter().collect()
    }
    
    pub fn is_anagram(&self, candidate: &str) -> bool {
        let candidate_lower = candidate.to_lowercase();
        candidate_lower != self.word && Self::sort_chars(&candidate_lower) == self.sorted_chars
    }
    
    pub fn find_anagrams<'a>(&self, candidates: &'a [&str]) -> HashSet<&'a str> {
        candidates
            .iter()
            .filter(|&&candidate| self.is_anagram(candidate))
            .cloned()
            .collect()
    }
}

这种面向对象的方式可以避免重复计算目标词的排序结果。

总结

通过 anagram 练习,我们学到了:

  1. 字符串处理:掌握了 Rust 中字符串和字符的基本操作
  2. 算法思维:学会了用排序和字符统计两种方法解决同一问题
  3. 集合操作:熟练使用 HashSet 进行数据收集和去重
  4. 生命周期:理解了 Rust 中的生命周期注解
  5. Unicode 处理:了解了 Unicode 字符的复杂性
  6. 测试驱动:通过丰富的测试用例确保实现的正确性

这些技能在实际开发中非常有用,特别是在处理文本数据、实现搜索功能和构建语言相关应用时。字母异位词虽然看起来简单,但它涉及到了字符串处理的许多核心概念,是学习 Rust 字符串操作的良好起点。

到此这篇关于Rust练习册之字母异位词与字符串处理方法技巧的文章就介绍到这了,更多相关Rust字母异位词与字符串内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 如何使用Rust的向量存储值列表

    如何使用Rust的向量存储值列表

    本文介绍了在Rust中使用向量存储值列表的方法,包括创建、更新、读取、遍历、存储多种类型以及内存释放等方面,向量是Rust中常用且强大的集合类型,熟练掌握其用法有助于编写高效且安全的代码
    2025-02-02
  • rust中智能指针的实现

    rust中智能指针的实现

    Rust智能指针通过所有权实现资源自动管理,包含Box、Rc、Arc、Mutex等类型,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2025-06-06
  • 使用win10 wsl子系统如何将 rust 程序静态编译为linux可执行文件

    使用win10 wsl子系统如何将 rust 程序静态编译为linux可执行文件

    这篇文章主要介绍了使用win10 wsl子系统如何将 rust 程序静态编译为linux可执行文件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-05-05
  • Rust数据类型之结构体Struct的使用

    Rust数据类型之结构体Struct的使用

    结构体是Rust中非常强大和灵活的数据结构,可以用于组织和操作各种类型的数据,本文就来介绍一下Rust数据类型之结构体Struct的使用,感兴趣的可以了解一下
    2023-12-12
  • rust多样化错误处理(从零学习)

    rust多样化错误处理(从零学习)

    一个优秀的项目,错误处理的优雅性是至关重要的,而rust,anyhow creat是绕不过去的一个,今天我们来研究下,怎么使用它,帮助我们写出更优雅的代码,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2023-11-11
  • Rust使用Sled添加高性能嵌入式数据库

    Rust使用Sled添加高性能嵌入式数据库

    这篇文章主要为大家详细介绍了如何在Rust项目中使用Sled库,一个为Rust生态设计的现代、高性能嵌入式数据库,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-03-03
  • Rust实现面向对象的方法

    Rust实现面向对象的方法

    这篇文章主要介绍了Rust实现面向对象的方法,Rust 并不是面向对象的语言,但是面向对象的功能都可以通过自身的特点来实现,本文通过示例代码给大家详细讲解,需要的朋友可以参考下
    2022-10-10
  • Rust语言之结构体和枚举的用途与高级功能详解

    Rust语言之结构体和枚举的用途与高级功能详解

    Rust 是一门注重安全性和性能的现代编程语言,其中结构体和枚举是其强大的数据类型之一,了解结构体和枚举的概念及其高级功能,将使你能够更加灵活和高效地处理数据,本文将深入探讨 Rust 中的结构体和枚举,并介绍它们的用途和高级功能
    2023-10-10
  • Rust安装的图文教程

    Rust安装的图文教程

    本文主要介绍了Rust安装的图文教程,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-06-06
  • Rust中实例化动态对象的示例详解

    Rust中实例化动态对象的示例详解

    这篇文章主要为大家详细介绍了Rust中实例化动态对象的多种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-02-02

最新评论