后缀树 最长公共子串

1. 代码如下:

#include <iostream> #include <string> #include <set> using namespace std; const int TREE_WIDTH = 256; const int WORDLENMAX = 128; // for the longest common substring problem // WHITE means dont contain neither 1 or 2 // RED means contain 1 $ // BLUE means contain 2 # // BLACK means contain both enum COLOR { WHITE = 0, RED = 1, BLUE = 2, BLACK = 3 }; class TrieNode { public: COLOR color; TrieNode *parent; TrieNode *next[TREE_WIDTH]; TrieNode(TrieNode* p = NULL, COLOR c = WHITE) : parent(p), color(c) { for (int i = 0; i < TREE_WIDTH; i++) { next[i] = NULL; } } }; class Trie { public: Trie() { root = new TrieNode(); } int insert(const char* word); void longestCommonSubstring() { longestCommonSubstring(root); } private: void travel(TrieNode *p); void longestCommonSubstring(TrieNode *p); TrieNode* root; }; int Trie::insert(const char *word) { int i; TrieNode *curr, *newnode; if (word[0] == '/0') { return 0; } string s(word); COLOR c = WHITE; switch(s[s.size()-1]) { case '$': c = RED; break; case '#': c = BLUE; break; default: break; } //cout << "inserting " << s << " with color " << c << endl; curr = root; curr->color = (COLOR)(curr->color | c); for (i = 0;; ++i) { if (curr->next[ word[i] ] == NULL) { newnode = new TrieNode(curr); curr->next[ word[i] ] = newnode; } if (word[i] == '/0') { break; } curr->color = (COLOR)(curr->color | c); curr = curr->next[ word[i] ]; } return 0; } bool myfn(string i, string j) { return i.size() < j.size(); } static set<string> s; void Trie::longestCommonSubstring(TrieNode* p) { travel(p); cout << *max_element(s.begin(), s.end(), myfn) << endl; } void addWord(const char *str) { s.insert(string(str)); } void Trie::travel(TrieNode *p) { static char worddump[WORDLENMAX+1]; static int pos=0; int i; if (p == NULL) { return; } if (p->color != BLACK) { worddump[pos - 1]='/0'; addWord(worddump); } if(p->color == BLACK) { for (i=0; i<TREE_WIDTH; ++i) { worddump[pos++]=i; travel(p->next[i]); pos--; } } return; } int main(void) { Trie trie; string s1, s2; cin >> s1; cin >> s2; cout << "finding the longest common substring of " << s1 << " and " << s2 << " :"<< endl; s1 = s1 + "#"; s2 = s2 + "$"; for (unsigned int i = 0; i < s1.size(); i++) { trie.insert(s1.substr(i).c_str()); } for (unsigned int i = 0; i < s2.size(); i++) { trie.insert(s2.substr(i).c_str()); } trie.longestCommonSubstring(); return 0; }

2. 运行结果如下:

/home/a/j/nomad2:cat input|./a.out finding the longest common substring of dababced and bababcff : ababc

3. 说明: 在建完后缀树后,对所有节点做标记,然后寻找深度最深的同时含有(#和$)的节点,复杂度为O(m+n).

### 使用字典树(Trie)实现最长公共子串 对于多个字符串的最长公共子串问题,一种有效的方法是利用字典树(Trie)。通过构建一个特殊的 Trie 结构并对其进行遍历分析,能够高效地找到这些字符串之间的最长公共子串。 #### 构建带有标记的 Trie 树 为了处理这个问题,在建立 Trie 的过程中需要对节点做特殊标记。每当插入一个新的单词时,如果当前路径上的某个节点已经被其他不同源字符串访问过,则在此处打上标志位表示这是一个潜在的共同起点[^1]。 ```python class TrieNode: def __init__(self): self.children = {} self.string_from = set() # 记录经过此节点的不同原字符串索引集合 def insert(trie_root, word, index): node = trie_root for char in word: if char not in node.children: node.children[char] = TrieNode() node = node.children[char] node.string_from.add(index) trie_root = TrieNode() for i, string in enumerate(strings_list): # strings_list 是输入的多条待比较字符串列表 insert(trie_root, string, i) ``` #### 寻找最大深度共有分支 完成上述操作后,整个 Trie 就包含了所有输入字符串的信息,并且每个内部节点都记录了哪些原始字符串共享这条边。接下来的任务是从根出发沿着那些被至少两个以上来源所使用的路径向下走直到不能再前进为止;此时到达的位置对应的字符串片段即为所需寻找的一个候选答案之一[^3]。 ```python def find_lcs(node, current_path=""): common_prefixes = [] if len(node.string_from) >= 2 and node != trie_root: lcp_length = sum([len(current_path)] * (len(node.string_from)-1)) global max_lcp_length, result_str if lcp_length > max_lcp_length: max_lcp_length = lcp_length result_str = current_path for child_char, next_node in node.children.items(): new_current_path = current_path + child_char find_lcs(next_node, new_current_path) max_lcp_length = -float('inf') result_str = "" find_lcs(trie_root) print(f"The Longest Common Substring is '{result_str}' with length {max_lcp_length}") ``` 这种方法不仅直观而且易于理解,但在实际应用中可能不是最高效的解决方案。更先进的技术如后缀数组或后缀自动机可能会提供更好的性能表现[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值