suffix-tree教程(个人总结)

背景

在计算机科学和生物信息学中,字符串处理是一个非常重要的领域。无论是搜索引擎、基因序列分析,还是压缩算法,都离不开高效的字符串处理。传统的字符串匹配算法,如暴力搜索、Knuth-Morris-Pratt (KMP) 算法和 Boyer-Moore 算法,虽然在特定场景下表现优异,但在处理大规模数据时常显得捉襟见肘。后缀树作为一种高级数据结构,以其高效的构建和查询性能,成为处理复杂字符串问题的利器。

什么是后缀树?

后缀树是一种特殊的树结构,用于表示一个字符串的所有后缀。给定一个长度为 n 的字符串 S,其后缀树是一个有根的有向树,包含 n 个叶子节点,每个叶子节点对应 S 的一个后缀。每个内部节点(除根节点外)至少有两个孩子节点,每条边都标记有 S 的一个非空子串。同一节点的两条边所标记的子串不能以相同的字符开头。后缀树的关键属性是,从根到叶子的路径所连接的边标记拼接起来正好是 S 的一个后缀。

优势与劣势

优势
  1. 快速构建:使用 Ukkonen 算法,后缀树可以在 O(n) 时间内构建。
  2. 高效查询:后缀树允许在 O(m) 时间内进行子串搜索,其中 m 是查询子串的长度。
  3. 丰富的应用:后缀树在子串搜索、模式匹配、最长重复子串和最长公共子串等问题上表现出色。
  4. 空间优化:虽然后缀树的空间复杂度为 O(n),但通过后缀数组等优化手段,可以进一步降低空间消耗。
劣势
  1. 空间消耗较大:在最坏情况下,后缀树的空间复杂度为O(n2),实际应用中通常为 O(n)。
  2. 实现复杂:Ukkonen 算法的实现较为复杂,对初学者有一定难度。
  3. 特定场景适用:后缀树主要用于字符串处理问题,对于其他类型的数据处理,可能不如其他数据结构高效。

后缀树的构建

后缀树的构建可以通过 Ukkonen 算法在 O(n) 时间内完成。以下是构建后缀树的详细步骤:

初始化

从一个仅包含根节点的空树开始。初始化活动点(active point),包括活动节点(active node)、活动边(active edge)和活动长度(active length)。

逐字符插入

对字符串中的每个字符,将对应的后缀插入到树中。每次插入新字符时,更新活动点并应用适当的扩展规则:

  1. 扩展规则 1:在活动点后插入一个新的边。
  2. 扩展规则 2:在活动点后扩展现有的边。
  3. 扩展规则 3:创建一个新的内部节点,并分裂现有的边。
活动点更新

根据扩展后的新状态,更新活动点的位置和状态。如果活动点在根节点且活动长度大于0,则将活动长度减1,并将活动边向前移动一位。如果活动点不是根节点,则将活动点移动到其后缀链接。

示例

以下是构建字符串 BANANA 的后缀树的详细过程:

  1. 初始化:从一个仅包含根节点的空树开始。
  2. 插入后缀
    • 插入 A:
    Root└── A
    
    • 插入 NA:
    Root└── A└── NA
    
    • 插入 ANA:
    Root└── A└── NA└── N└── A
    
    • 插入 NANA:
    Root└── A└── NA└── N└── A└── NA
    
    • 插入 ANANA:
    Root└── A└── N└── ANA└── N└── A└── NA
    
    • 插入 BANANA:
    Root└── A└── N└── ANA└── B└── ANANA└── N└── A└── NA
    

Ukkonen 算法

Ukkonen 算法是一个在线算法,通过逐步扩展后缀树来处理字符串中的每个字符。该算法的核心思想是维护一个活动点,通过该活动点跟踪当前正在处理的后缀。每次插入新字符时,算法根据当前活动点的位置和状态选择适当的规则进行处理。

详细步骤
  1. 初始化:创建一个根节点,并将活动点设置为根节点。
  2. 逐字符扩展:对字符串中的每个字符,执行以下步骤:
    • 扩展规则:根据当前活动点的位置和状态选择适当的扩展规则:
      • 规则 1:在活动点后插入一个新的边。
      • 规则 2:在活动点后扩展现有的边。
      • 规则 3:创建一个新的内部节点,并分裂现有的边。
    • 活动点更新:根据扩展后的新状态,更新活动点的位置和状态。
示例代码

以下是 Ukkonen 算法的 Python 实现:

class SuffixTreeNode:def __init__(self):self.children = {}self.suffix_link = Noneself.start = Noneself.end = Noneclass SuffixTree:def __init__(self, text):self.text = textself.root = SuffixTreeNode()self.build_suffix_tree()def build_suffix_tree(self):n = len(self.text)self.root.end = -1self.root.suffix_link = self.rootactive_node = self.rootactive_edge = -1active_length = 0remainder = 0for i in range(n):last_new_node = Noneremainder += 1while remainder > 0:if active_length == 0:active_edge = iif self.text[active_edge] not in active_node.children:leaf = SuffixTreeNode()leaf.start = ileaf.end = nactive_node.children[self.text[active_edge]] = leafif last_new_node:last_new_node.suffix_link = active_nodelast_new_node = Noneelse:next_node = active_node.children[self.text[active_edge]]edge_length = next_node.end - next_node.startif active_length >= edge_length:active_edge += edge_lengthactive_length -= edge_lengthactive_node = next_nodecontinueif self.text[next_node.start + active_length] == self.text[i]:if last_new_node:last_new_node.suffix_link = active_nodeactive_length += 1breaksplit = SuffixTreeNode()split.start = next_node.startsplit.end = next_node.start + active_lengthactive_node.children[self.text[active_edge]] = splitleaf = SuffixTreeNode()leaf.start = ileaf.end = nsplit.children[self.text[i]] = leafnext_node.start += active_lengthsplit.children[self.text[next_node.start]] = next_nodeif last_new_node:last_new_node.suffix_link = splitlast_new_node = splitremainder -= 1if active_node == self.root and active_length > 0:active_length -= 1active_edge = i - remainder + 1elif active_node != self.root:active_node = active_node.suffix_linkdef traverse_tree(self, node, suffixes, current_suffix):if not node.children:suffixes.append(current_suffix)returnfor char, child in node.children.items():self.traverse_tree(child, suffixes, current_suffix + self.text[child.start:child.end])def get_suffixes(self):suffixes = []self.traverse_tree(self.root, suffixes, "")return suffixestext = "BANANA"
st = SuffixTree(text)
suffixes = st.get_suffixes()
print(suffixes)

后缀树的优化

虽然后缀树具有许多优点,但其空间复杂度可能较高。为了优化空间,可以考虑以下几种方法:

  1. 后缀数组:后缀数组是一种空间更为紧凑的数据结构,可以用来替代后缀树。在某些应用中,后缀数组能够提供类似的功能,并具有更低的空间开销。
  2. 增强后缀数组:增强后缀数组结合了后缀数组和后缀树的优点,提供了一种高效且空间优化的解决方案。
  3. 节点压缩:通过合并后缀树中的某些节点,减少节点数量,从而降低空间复杂度。

后缀数组

后缀数组是一个存储字符串所有后缀的数组,每个后缀按字典顺序排序。构建后缀数组的时间复杂度为O(nlogn),并且通过使用 Kasai 等人的算法,可以在 O(n) 时间内构建出后缀数组的高度数组(LCP 数组)。

示例代码

以下是构建后缀数组的 Python 实现:

def build_suffix_array(text):n = len(text)suffixes = sorted([text[i:] for i in range(n)])suffix_array = [n - len(suffix) for suffix in suffixes]return suffix_arraytext = "BANANA"
suffix_array = build_suffix_array(text)
print(suffix_array)

应用实例

假设您需要在文本 BANANA 中查找模式 ANA 的所有出现位置。可以按照以下步骤使用后缀树:

  1. 构建文本 BANANA 的后缀树。
  2. 遍历树,沿着标记为 ANA 的边进行搜索。
  3. 如果在消耗完模式后到达一个节点,则该节点下的叶子节点表示模式在文本中的起始位置。

后缀树的更多应用

除了子串搜索、最长重复子串和最长公共子串外,后缀树在其他字符串处理问题中也表现出色:

  1. 字符串压缩:后缀树可以用于构建 BWT(Burrows-Wheeler Transform),这是许多字符串压缩算法的核心。
  2. 基因序列分析:在生物信息学中,后缀树被广泛用于基因序列的匹配和分析。
  3. 文档相似性检测:通过构建文档的后缀树,可以快速检测两个文档之间的相似度。

结论

后缀树是处理各种字符串处理问题的强大数据结构。通过了解其构建方法、性质和应用,可以显著提升解决复杂字符串相关问题的能力。本文详细介绍了后缀树的构建、性质、应用及其优化方法,并提供了丰富的示例和代码实现,旨在帮助读者全面而深入地理解后缀树。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/19864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android14 WMS-IWindow介绍

IWindow是很重要的,官方介绍是API back to a client window that the Window Manager uses to inform it of interesting things happening. 也就是说是是用于WMS回调客户端的,当窗口有一些改变时,WMS及时调用客户端接口,让客户端…

Ubuntu22.04之解决:忘记登录密码(二百三十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

gpt-4o api申请开发部署应用:一篇全面的指南

利用 GPT-4o API 开发创新应用:一篇全面的指南 OpenAI 的 GPT-4o 是一款集成了音频、视觉和文本处理能力的多模态人工智能模型,它的出现代表了人工智能领域的重大进步。在本篇文章中,我们将详细介绍如何通过 OpenAI API 使用 GPT-4o&#xf…

html中 table的 colspan和rowspan

Colspan 单元格跨越多列; Rowspan 单元格跨越多行 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title></title> </head> <body><h4>单元格跨两列:</h4> <table border"1"&…

蓝桥杯java组-字符串输入输出处理

题目描述&#xff1a;字符串的输入输出处理。 输入&#xff1a;第一行是一个正整数N&#xff0c;最大为100。之后是多行字符串&#xff08;行数大于N&#xff09;&#xff0c; 每一行字符串可能含有空格&#xff0c;字符数不超过1000。 输出&#xff1a;先将输入中的前N行字符…

云动态摘要 2024-05-31

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [1.5折起]年中盛惠--AI分会场 腾讯云 2024-05-30 人脸核身、语音识别、文字识别、数智人、腾讯混元等热门AI产品特惠&#xff0c;1.5折起 云服务器ECS试用产品续用 阿里云 2024-04-14 云…

鸿蒙开发接口媒体:【@ohos.multimedia.medialibrary (媒体库管理)】

媒体库管理 说明&#xff1a; 该组件从API Version 6开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 发前请熟悉鸿蒙开发指导文档&#xff1a; gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 导入模块 …

2.4 Docker部署JDK

2.4 Docker部署JDK jdk17部署&#xff08;自定义镜像&#xff09; 1.在官网上下载jdk-17_linux-x64_bin.tar.gz&#xff0c;并安装到/usr/local目录下 cd /usr/local2.创建Dockerfile vim Dockerfile# 基于官方的Ubuntu 20.04镜像作为基础镜像 FROM ubuntu:20.04# 设置环境…

【python深度学习】——大型工程项目管理以及互相导入

【python深度学习】——大型工程项目管理以及互相导入 1. 工程项目中常见的文件组织形式2. python中的“包”、“模块”、与__init__.py2.1 概念理解2.2 \__init__py的使用3. 包的导入——相对导入与绝对导入3.1 相对导入3.1.1 相对导入的语法3.1.2 相对导入的使用注意事项与常…

Attentive Transfer Entropy to Exploit Transient Emergence of Coupling Effect

本文可以采用以下六个标准: 目标:指的是研究的基本目的。 耦合网络重建专注于揭示网络中变量之间潜在的连接结构,确定它们是如何相互关联的。因果发现更进一步,不仅识别连接,还确定变量之间的因果关系和方向。信息传递测量量化变量之间流动的信息量,表明它们影响的强度和…

二维数组传参时不用二级指针接收

先放结论&#xff1a; 1. 二维数组数组名指向的类型是 int [x] 类型&#xff0c;int** 指针指向类型是 int* &#xff0c;如果用二级指针接收会导致访问错误&#xff0c;因为 int [x] 类型和 int* 类型不同。 2. 指向什么类型的指针1就按照该类型的字节数1移动。 最近在学…

初识java——javaSE(8)异常

文章目录 一 异常的概念与体系结构1.1 什么是异常&#xff1f;1.2 异常的体系结构&#xff01;1.3 编译时异常与运行时异常与Error编译时异常&#xff1a;异常声明&#xff1a;throws关键字 运行时异常&#xff1a;什么是Error? 二 处理异常2.1 异常的抛出&#xff1a;throw(注…

容器多机部署eureka及相关集群服务出现 Request execution failed with message: AuthScheme is null

预期部署方案&#xff1a;两个eureka三个相关应用 注册时应用出现&#xff1a;Request execution failed with message: Cannot invoke “Object.getClass()” because “authScheme” is null&#xff0c;一开始认为未正确传递eureka配置的账户密码&#xff0c;例&#xff1a;…

5.23R语言-参数假设检验

理论 方差分析&#xff08;ANOVA, Analysis of Variance&#xff09;是统计学中用来比较多个样本均值之间差异的一种方法。它通过将总变异分解为不同来源的变异来检测因子对响应变量的影响。方差分析广泛应用于实验设计、质量控制、医学研究等领域。 方差分析的基本模型 方差…

重庆人文科技学院建立“软件安全产学研基地”,推动西南地区软件安全发展

5月29日&#xff0c;重庆人文科技学院与开源网安签订了《产学研校企合作协议》&#xff0c;并举行了“重庆人文科技学院产学研基地”授牌仪式&#xff0c;此次合作不仅深化了双方在软件安全领域的产学研紧密联结&#xff0c;更是对川渝乃至西南地区软件供应链安全发展起到重要的…

力扣linkedlist

反转链表、 public class reverseList { // 1->2->3->o 、 o<-1<-2<-3public ListNode reverseList(ListNode head){//反转链表ListNode prevnull;ListNode currhead;while(curr!null){ListNode nextcurr.next;curr.nextprev;prevcurr;currnext;}retu…

AI免费插件 批量条码大师,支持100多种条码类型

没想到在网上看到一款和之前 悟空条码 类似的条码插件&#xff0c;叫批量条码大师&#xff0c;他做的比 悟空条码 功能更强&#xff0c;界面更美观&#xff0c;特分享出来给大家。 本插件采用了BWIPJS条码库&#xff0c;支持110种条码、二维码的生成; 支持批量生成&#xff0c;…

爱堡集团数智掘金—共绘上市蓝图

&#xff08;本台记者报&#xff09;2024年5月26日爱堡集团在浙江省杭州市上城区瑞莱克斯大酒店隆重召开规模达500人的盛会。这场聚焦智慧与创新的会议&#xff0c;旨在加速爱堡集团的数智化转型进程&#xff0c;并为其上市之路绘制蓝图&#xff0c;吸引了众多行业领袖和媒体的…

Qt 插件机制使用及原理

目录 1.引言 2.插件原理 3.插件实现 3.1.定义一个接口集(只有纯虚函数的类) 3.2.实现接口 4.插件的加载 4.1.静态插件 4.1.1.静态插件实现方式 4.1.2.静态插件加载的过程 4.1.3.示例 4.2.动态插件 4.2.1.动态插件的加载过程 5.定位插件 6.插件开发的优势 7.总结…

GPT-4o有点坑

GPT-4o有点坑 0. 前言1. GPT-4o简介2. GPT-4o带来的好处2.1 可以上传图片和文件2.2 更丰富的功能以及插件 3. "坑"的地方3.1 使用时间短3.2 GPT-4o变懒了 4. 总结 0. 前言 原本不想对GPT-4o的内容来进行评论的&#xff0c;但是看了相关的评论一直在说&#xff1a;技…