查找作为其他字符串前缀的字符串

时间:2012-07-09 14:29:31

标签: algorithm data-structures language-agnostic trie

这是一个面试问题。给定一些字符串找到这样的字符串,这些字符串是其他字符串的前缀。例如,给定strings = {"a", "aa", "ab", abb"},结果为{"a", "ab"}

最简单的解决方案是对字符串进行排序,如果第一个字符串是第二个字符串的前缀,则检查每对后续两个字符串。算法的运行时间是排序的运行时间。

我想还有另一种解决方案,它使用trie,并且具有复杂度O(N),其中N是字符串的数量。你能建议这样的算法吗?

2 个答案:

答案 0 :(得分:6)

关于Trie,我有一个关于复杂性O(N)的想法: 你从空Trie开始。 你逐个说话,并向Trie添加单词。 在向Trie添加一个单词(让我们称之为Wi)后,有两种情况需要考虑:

  1. Wi是您之前添加的某些字词的前缀。 如果在添加单词Wi时没有向Trie添加任何节点,则该声明为真。 在这种情况下,Wi是我们解决方案的前缀和部分。
  2. 之前添加的一些词是Wi的前缀。 如果您通过表示之前添加的某个单词结尾的节点(让我们称之为单词Wj),则该语句为真。在这种情况下,Wj是Wi的前缀,也是我们解决方案的一部分。
  3. 更多细节(伪代码):

    for word in words
        add word to trie
        if size of trie did not change then   // first case
            add word to result
        if ending nodes found while adding word   // second case
            add words defined by those nodes to result
    return result
    

    向Trie添加新词:

    node = trie.root();
    for letter in word
        if node.hasChild(letter) == false then   // if letter doesnt exist, add it
            node.addChild(letter)
        if letter is last_letter_of_word then   // if last letter of word, store that info
            node.setIsLastLetterOf(word)
        node = node.getChild(letter)    // move
    

    在添加新单词时,您还可以检查是否通过了代表其他单词的最后一个字母的任何节点。 我描述的算法的复杂性是O(N)。 另一个重要的事情是,通过这种方式,您可以知道Wi在其他单词前加多少次,这可能很有用。

    {aab,aaba,aa}的示例: 绿色节点是检测为情况1的节点。 红色节点是检测为情况2的节点。 每列(trie)是一步。一开始trie是空的。 黑色箭头显示我们在该步骤中访问(添加)的节点。 代表某个单词的最后一个字母的节点将该单词写在括号中。

    enter image description here

    1. 在步骤1中,我们添加单词aab。
    2. 在步骤2中,我们添加单词aaba,识别一个案例2(单词aab)并在结果中添加单词aab。
    3. 在步骤3中,我们添加单词aa,识别单词1并在结果中添加单词aa。
    4. 最后我们有结果= {aab,aa}这是正确的。

答案 1 :(得分:3)

原始答案是正确的:是a的字符串b子字符串(误读)。

使用trie,你可以在第一次迭代中简单地添加所有字符串,在第二次迭代中,开始阅读每个单词,让它为w。如果您找到一个单词表明您已完成阅读但未到达字符串终结符(通常为$),则会在trie中找到某个节点v
通过从v执行DFS,您可以获得w为其前缀的所有字符串。

高级伪代码:

t <- new trie
for each word w:
   t.add(w)
for each word w:
  node <- t.getLastNode(w)
  if node.val != $
     collection<- DFS(node) (excluding w itself)
     w is a prefix of each word in collection

注意:为了优化它,您可能需要做一些额外的工作:如果ab的前缀,而bc的前缀,然后ac的前缀,因此 - 当您执行DFS时,如果您到达某个已经搜索过的节点 - 只需将其字符串附加到当前前缀即可。
尽管如此,因为可能存在二次数的可能性("a", "aa", "aaa", ....),所以它们都需要二次时间。


原始答案:查找ab子字符串

建议的解决方案以二次复杂度运行,您需要检查每两对,为您提供O(n* (n-1) * |S|)

您可以在第一次迭代中从字符串构建suffix tree,并在第二次迭代中检查每个字符串是否是另一个字符串的非平凡条目(不是其自身)。
此解决方案为O(n*|S|)