📜  Ukkonen的后缀树构造–第2部分(1)

📅  最后修改于: 2023-12-03 15:35:27.379000             🧑  作者: Mango

Ukkonen的后缀树构造-第2部分

第1部分中,我们介绍了Ukkonen的后缀树构造算法的基本思想,重点介绍了相邻相似点的概念和Suffix links的作用。在本文中,我们将详细介绍这个算法的实现细节。

边界条件

首先,我们需要设定一些边界条件。假设我们正在构造一棵后缀树T,已经插入了前i个字符(i>0),需要插入第i+1个字符c[i]。我们将根节点标记为T.root,n表示字符串的总长度。为了处理插入过程中可能出现的多种情况,我们还需要设置以下一些边界条件:

  1. 在任何时候,如果当前节点没有可出发的边,c[i]应该插在该节点上。

  2. 如果当前节点有指向下一个字符c[i]的边,那么称之为active edge。

  3. 如果当前节点的active edge的长度小于该边对应子树中剩余的字符数,则称其为active_point。

  4. 每次插入字符后,我们需要更新active point。同时,这还表示了我们插入过程中所经过的路径。

  5. 如果插入字符时需要沿着Suffix link下降,我们需要同时更新active point和active length。

插入新字符

在已经设置好边界条件的基础上,我们可以开始执行后缀树的构造过程。下面是主要的流程:

  1. 首先,我们将根节点设为当前节点,并将active length设为0.

    active_node = T.root
    active_length = 0
    
  2. 我们需要逐个字符地扫描字符串,对于每个字符c[i],执行以下操作:

    • 如果在当前节点的active edge上找到了字符c[i],则更新active point、active length并返回。

    • 如果当前节点没有以c[i]开头的子边,则需要创建一个新节点和对应的边,并将其插入到树上。

    • 如果当前节点存在以c[i]开头的子边,但active length等于该边的长度(即从该节点出发可以直接到达下一节点),则将当前节点更新为该子边指向的节点,并将active length重置为0。

    • 否则,我们需要在该子边的current extension上继续扩展

      if active_edge in active_node.edges.keys():
          # case 1: active edge exists
          pass
      else:
          # case 2: active edge not exists
          pass
      
      if active_length == 0:
          # case 3: active edge's length is 0
          pass
      else:
          # case 4: continue extension from current edge
          pass
      

在这个过程中,我们需要不断检查子串是否以前缀存在,并在树的节点之间移动。我们在之前介绍过的substring function将会在这个过程中得到广泛的应用。

下面是完整的Python代码实现:

class Node:
    def __init__(self, start_index, end_index):
        self.start_index = start_index
        self.end_index = end_index
        self.edges = {}
        
    def add_edge(self, edge_label, next_node):
        self.edges[edge_label] = next_node

class SuffixTree:
    def __init__(self, string):
        self.string = string
        self.root = Node(-1, -1)
        self.end_index = len(string) - 1
        self.superscript = "$"
        
    def build_suffix_tree(self):
        for i in range(self.end_index + 1):
            self.add_prefix(self.string[i:])
            
    def add_prefix(self, prefix):
        active_node = self.root
        active_edge = ""
        active_length = 0
        i = 0
        while i < len(prefix):
            c = prefix[i]
            if active_length == 0:
                active_edge = c
                
            if active_edge in active_node.edges.keys():
                child_node = active_node.edges[active_edge]
                child_label = self.string[child_node.start_index]
                
                # case 1: active edge exists
                if c == child_label:
                    active_length += 1
                    break
                # case 4: continue extension from current edge
                else:
                    pass
            else:
                # case 2: active edge not exists
                pass

            i += 1
总结

通过以上介绍,我们可以初步理解Ukkonen的后缀树构造算法的实现过程。后缀树通常被认为是寻找匹配模式的全文本模式搜索中的最有效的数据结构之一,同时也是字符串处理中一个非常重要的知识点。因此,在使用前缀树的过程中,需要考虑到诸如复杂度等方面的问题。在实际应用中,我们也可以用更加高效的方法实现这些操作,以使算法更加实用。