使用后缀树的模式搜索


Trie树 − Trie树是一种基于树的数据结构,用于存储和检索动态字符串集。

压缩Trie树 − 压缩Trie树是Trie树的一种变体,用于存储和搜索动态字符串集。通过共享公共前缀来最小化内存使用。

在压缩Trie树中,只有单个子节点的节点与其父节点合并,将公共前缀压缩到单个边中。

后缀树 − 后缀树是一种用于字符串处理的数据结构,用于存储和搜索给定字符串的所有后缀。它以树状结构的形式表示字符串的所有可能后缀,其中每条边代表一个子字符串,每个节点代表字符串中的一个位置。根节点代表空字符串,叶子节点代表字符串的所有唯一后缀。

为给定字符串创建后缀树

  • 生成给定字符串的所有后缀。

  • 例如,单词“world”−

Suffixes of “world\0” are:
world\0
orld\0
rld\0
ld\0
d\0
\0
  • 将每个后缀作为单独的单词,创建一个压缩Trie树。

问题陈述

给定一个输入字符串“str”和一个模式字符串“ptr”。使用后缀树判断模式字符串“ptr”是否出现在输入字符串“str”中,以及它们出现的索引。

示例 1

Input: str = “aabcdaaabcdbabc”
ptr = “abc”
Output: 1, 7, 12

解释

模式“abc”出现在输入字符串的索引 1、7 和 12。

示例 2

Input: str = “minimization”
ptr = “ma”
Output: False

解释

模式“ma”未在输入字符串中找到。

解决方案方法

为了在后缀树中搜索ptr,我们首先查看模式的第一个字符,并将其与根节点的子节点进行匹配。如果匹配,则我们在子节点上递归搜索。但是,如果在任何时候模式与子节点不匹配,则模式不存在于字符串中。

伪代码

class Node
   children[256]: Node array
   ind: List of integers

   constructor()
      ind <- create new empty list of integers
      for i from 0 to 255
         children[i] <- NULL

   function insertSuffix(suffix: string, index: integer)
      ind.push_back(index)
      if suffix.length() > 0
         cIndex <- suffix.at(0)
         if children[cIndex] is NULL
            children[cIndex] <- create new Node
         children[cIndex].insertSuffix(suffix.substr(1), index + 1)

   function search(pat: string): List of integers
      if pat.length() is 0
         return ind
      if children[pat.at(0)] is not NULL
         return children[pat.at(0)].search(pat.substr(1))
      else
         return NULL


class SuffixTree
   root: Node

   constructor(txt: string)
      root <- create new Node
      for i from 0 to txt.length() - 1
         root.insertSuffix(txt.substr(i), i)

   function search(ptr: string)
      ans <- root.search(ptr)
      if ans is NULL
         print "Pattern not found"
      else
         for each i in ans
            print "Pattern found at position " + (i - ptr.length())

示例:C++ 实现

以下代码使用后缀树搜索字符串中的模式。

#include <bits/stdc++.h>
using namespace std;

// Defining node of the Suffix tree
class Node{
private:
   Node *children[256];
   list<int> *ind;
public:
   Node(){
      ind = new list<int>;
      for (int i = 0; i < 256; i++) {
         children[i] = NULL;
      }
   }
   // Inserting new suffix to the tree
   void insertSuffix(string suffix, int index){
      ind->push_back(index);
      if (suffix.length() > 0){
         char cIndex = suffix.at(0);
         if (children[cIndex] == NULL)
            children[cIndex] = new Node();
         children[cIndex]->insertSuffix(suffix.substr(1), index + 1);
      }
   }
   // Pattern Searching in subtree
   list<int> *search(string pat){
      if (pat.length() == 0)
         return ind;
      if (children[pat.at(0)] != NULL)
         return (children[pat.at(0)])->search(pat.substr(1));
      else
         return NULL;
   }
};

// Defination of Suffix Tree
class SuffixTree {
private:
   Node root;
public:
   SuffixTree(string txt){
      for (int i = 0; i < txt.length(); i++)
         root.insertSuffix(txt.substr(i), i);
   }
   // Function for searching a pattern in the tree
   void search(string ptr){
      list<int> *ans = root.search(ptr);
      if (ans == NULL)
         cout << "Pattern not found" << endl;
      else {
         list<int>::iterator i;
         int ptrLength = ptr.length();
         for (i = ans->begin(); i != ans->end(); i++){
            cout << "Pattern found at position " << *i - ptrLength << endl;
         }
      }
   }
};
int main(){
   string str = "aabcdaaabcdbabc";
   string ptr = "abcx";
   SuffixTree Tree(str);
   cout << "Searching for " << ptr << endl;
   Tree.search(ptr);
   return 0;
}

输出

Searching for abcx
Pattern not found

时间复杂度

后缀树构建 − O(N^2),其中N是输入字符串的长度,这是最坏情况下的时间复杂度。

模式搜索 − O(M),其中M是模式的长度。

空间复杂度

后缀树构建 − O(N^2),其中N是输入字符串的长度,这是最坏情况下的空间复杂度。

模式搜索 − O(1)

结论

总之,后缀树是一种强大的数据结构,可以有效地存储和操作字符串。它允许进行各种与字符串相关的操作,包括子字符串搜索、模式匹配以及前缀/后缀查询。使用后缀树在字符串中进行模式搜索是一种有效的方法。提供的解决方案以O(M)的时间复杂度和O(1)的空间复杂度解决了问题,其中M是模式字符串的大小。

更新于:2023年11月3日

458 次浏览

开启你的职业生涯

完成课程获得认证

开始学习
广告