Rust回文树详解(从零开始掌握回文自动机的Rust实现)

简介: 本教程详解如何用Rust语言实现回文树(回文自动机),涵盖核心概念、节点结构设计与动态构建过程。通过清晰的代码示例,带你掌握高效处理回文子串统计的方法,适合Rust初学者和算法爱好者学习与实践。

在字符串处理领域,Rust回文树(也称为回文自动机)是一种高效的数据结构,用于快速统计和查询字符串中所有不同的回文子串。本教程将手把手教你如何用Rust语言实现一个完整的回文树,即使你是Rust初学者也能轻松上手。

什么是回文树?

回文树(Palindromic Tree),又称Eertree,是由Mikhail Rubinchik在2015年提出的一种数据结构。它能够在线性时间内构建,并支持动态添加字符、统计不同回文子串数量、查询最长回文后缀等操作。

回文树的核心思想

回文树包含两类节点:

  • 奇根节点:代表长度为-1的虚拟回文串(用于处理奇数长度回文)
  • 偶根节点:代表长度为0的空回文串(用于处理偶数长度回文)

每个节点存储以下信息:

  • 回文串的长度
  • 指向其最长回文后缀的fail指针
  • 子节点映射(通过字符跳转)
  • 出现次数(可选)

Rust实现步骤

下面我们用字符串处理Rust的方式逐步实现回文树。

1. 定义节点结构

#[derive(Default)]struct Node {    len: i32,               // 回文串长度    fail: usize,            // fail指针    next: [usize; 26],      // 子节点(假设只处理小写字母)    count: usize,           // 出现次数}impl Node {    fn new(len: i32) -> Self {        Node {            len,            fail: 0,            next: [0; 26],            count: 0,        }    }}

2. 定义回文树结构

struct PalindromicTree {    nodes: Vec<Node>,    last: usize,            // 当前最长回文后缀对应的节点    s: Vec<u8>,             // 输入字符串的字符数组    n: usize,               // 当前处理到的位置}impl PalindromicTree {    fn new() -> Self {        let mut nodes = Vec::new();        // 节点0:偶根(长度0)        nodes.push(Node::new(0));        // 节点1:奇根(长度-1)        nodes.push(Node::new(-1));                // 奇根的fail指向自己        nodes[1].fail = 1;        // 偶根的fail指向奇根        nodes[0].fail = 1;                PalindromicTree {            nodes,            last: 0,            s: vec![0], // 添加哨兵            n: 0,        }    }}

3. 实现扩展函数

关键在于找到当前位置能形成的新回文串:

impl PalindromicTree {    fn extend(&mut self, c: u8) {        self.s.push(c);        self.n += 1;                let ci = (c - b'a') as usize;        let mut cur = self.last;                // 找到满足 s[n - len[cur] - 1] == s[n] 的cur        while self.s[self.n - self.nodes[cur].len as usize - 1] != c {            cur = self.nodes[cur].fail;        }                if self.nodes[cur].next[ci] != 0 {            // 已存在该回文串            self.last = self.nodes[cur].next[ci];            self.nodes[self.last].count += 1;            return;        }                // 创建新节点        let new_node = self.nodes.len();        self.nodes.push(Node::new(self.nodes[cur].len + 2));        self.nodes[cur].next[ci] = new_node;                // 设置fail指针        if self.nodes[new_node].len == 1 {            // 长度为1,fail指向偶根            self.nodes[new_node].fail = 0;        } else {            // 找到最长回文后缀            let mut tmp = self.nodes[cur].fail;            while self.s[self.n - self.nodes[tmp].len as usize - 1] != c {                tmp = self.nodes[tmp].fail;            }            self.nodes[new_node].fail = self.nodes[tmp].next[ci];        }                self.last = new_node;        self.nodes[self.last].count = 1;    }}

4. 使用示例

fn main() {    let s = "ababa";    let mut tree = PalindromicTree::new();        for c in s.bytes() {        tree.extend(c);    }        // 不同回文子串数量 = 节点数 - 2(减去两个根节点)    println!("不同回文子串数量: {}", tree.nodes.len() - 2);        // 输出所有回文串长度    for (i, node) in tree.nodes.iter().enumerate() {        if i > 1 { // 跳过根节点            println!("回文串长度: {}, 出现次数: {}", node.len, node.count);        }    }}

为什么选择Rust实现回文自动机?

使用Rust算法实现回文树有诸多优势:

  • 内存安全:避免C++中常见的指针错误
  • 零成本抽象:性能接近C/C++
  • 强大的类型系统:编译期捕获错误
  • 所有权模型:自动管理内存,无需垃圾回收

总结

通过本教程,你已经掌握了Rust回文树的基本原理和完整实现。回文自动机是处理回文相关问题的强大工具,在竞赛编程和实际应用中都有广泛用途。建议你动手实践,尝试添加更多功能如输出具体回文串、处理Unicode字符等。

记住,掌握字符串处理Rust技巧不仅能提升算法能力,还能加深对Rust语言特性的理解。继续练习,你将成为Rust算法高手!

来源:

https://www.vpshk.cn/

相关文章
|
存储 编解码 算法
音视频之音频知识入门
信息论的观点来看,描述信源的数据是信息和数据冗余之和,即:数据=信息+数据冗余。音频信号在时域和频域上具有相关性,也即存在数据冗余。将音频作为一个信源,音频编码的实质是减少音频中的冗余。自然界中的声音非常复杂,波形极其复杂,通常我们采用的是脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。
1869 0
|
4月前
|
人工智能 运维 安全
技术深析快手直播安全事件:为什么大量违规直播“关不掉”?
快手直播安全事件暴露了高并发下账号权限、风控与审核系统的系统性失效。对测试开发而言,需从功能验证转向系统性防控,强化极端场景测试、高负载审核链路验证及熔断机制演练,提升对复杂风险的预判与拦截能力。
|
4月前
|
存储 人工智能 Cloud Native
玄晶引擎深度测评:Sora2+Coze+RPA三重赋能,AI内容生产工具的云原生进化
玄晶引擎升级,融合Sora2视频生成、Coze智能体调用与RPA自动化运营,打造云原生AI内容生产闭环。通过云边协同、低代码集成与多平台联动,实现从创意到转化的全链路提效,助力企业降本增效,提供阿里云生态下AI工具选型新范式。(239字)
301 5
|
4月前
|
人工智能 Cloud Native 安全
云上实践:从AI营销服务商的技术栈看企业MarTech选型与集成
在云原生与AI融合的营销时代,选型需兼顾技术集成能力。本文剖析链创AI、蓝色光标等五类服务商的技术架构与数据集成模式,提出API优先、数据合规、系统兼容等六大选型要点,助力企业实现安全、高效的技术对接,避免数据孤岛与技术债。
|
数据采集 缓存 Java
Python vs Java:爬虫任务中的效率比较
Python vs Java:爬虫任务中的效率比较
|
人工智能 自然语言处理 供应链
科技云报到:RPA怎么了?2025年或将强的可怕!
科技云报到:RPA怎么了?2025年或将强的可怕!
410 1
|
机器学习/深度学习 人工智能 监控
《智破光影迷宫:人工智能图像识别的进阶挑战》
在数字化时代,人工智能图像识别技术广泛应用于安防、医疗、交通等领域,显著提升了工作效率和准确性。然而,复杂背景与光照变化成为其发展的两大挑战。复杂背景使目标识别如大海捞针,光照变化则导致同一对象在不同条件下被误判。为应对这些挑战,深度学习技术如卷积神经网络(CNN)崭露头角,通过自动学习多层次特征提高识别精度。同时,光照归一化技术和数据增强等方法也有效提升了图像识别的鲁棒性。未来,随着算法优化和数据积累,图像识别技术将更加智能精准,为社会带来更多的便利与安全保障。
487 7
|
Oracle 关系型数据库 数据库连接
实时计算 Flink版操作报错合集之为什么使用StartupOptions.latest()能够正常启动而切换到StartupOptions.specificOffset时遇到报错
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
存储 SQL Java
MyBatis batchInsert 批量插入数据
MyBatis batchInsert 批量插入数据
1284 0
|
数据采集 前端开发 Python
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
396 0