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

Algorithm 查找作为其他字符串前缀的字符串,algorithm,data-structures,language-agnostic,trie,Algorithm,Data Structures,Language Agnostic,Trie,这是一个面试问题。给定一些字符串,查找这些字符串,它们是其他字符串的前缀。例如,给定字符串={a”,“aa”,“ab”,“abb”}结果是{a”,“ab”} 最简单的解决方案就是对字符串进行排序,如果第一个字符串是第二个字符串的前缀,则检查后续两个字符串的每一对。算法的运行时间就是排序的运行时间 我想还有另一种解决方案,它使用了一个trie,具有复杂性O(N),其中N是字符串的数量。你能建议这样一种算法吗?原始答案是正确的:字符串a是b的子字符串(误读) 使用trie,您只需在第一次迭代中向其添

这是一个面试问题。给定一些字符串,查找这些字符串,它们是其他字符串的前缀。例如,给定
字符串={a”,“aa”,“ab”,“abb”}
结果是
{a”,“ab”}

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


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

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

使用trie,您只需在第一次迭代中向其添加所有字符串,然后在第二次迭代中,开始读取每个单词,让它成为
w
。如果您找到一个已完成读取的单词,但没有到达字符串终止符(
$
),则可以到达trie中的某个节点
v

通过执行from
v
,您可以获得
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

t关于Trie,复杂性O(N),我有以下想法:
你从空的Trie开始。
你一个接一个地记单词,然后把单词添加到Trie中。
在Trie中添加一个单词(我们称之为单词Wi)后,需要考虑两种情况:

  • Wi是您之前添加的一些单词的前缀。 如果在添加单词Wi时未向Trie添加任何节点,则该语句为真。 在这种情况下,Wi是前缀,也是我们解决方案的一部分
  • 前面添加的一些单词是Wi的前缀。 若您通过表示前面添加的某个单词结尾的节点(让我们将该单词称为Wj),则该语句是正确的。在这种情况下,Wj是Wi的前缀,也是我们解决方案的一部分
  • 更多详细信息(伪代码):

    向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)。 另一件重要的事情是,通过这种方式,您可以知道word Wi在其他单词前面加了多少次前缀,这可能很有用

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

  • 在步骤1中,我们添加单词aab
  • 在步骤2中,我们添加单词aaba,识别一个案例2(单词aab),并将单词aab添加到结果中
  • 在步骤3中,我们添加单词aa,识别案例1,并将单词aa添加到结果中

  • 最后我们得到的结果是{aab,aa},这是正确的。

    我担心排序解决方案不会给您这样的运行时间。假设您有{a”,“aa”,“aaa”}-您可以在O(nlog(n))中对它们进行排序,但您仍然需要检查“a”是否是“aa”的前缀,“a”是“aaa”的前缀,“aa”是“aaa”的前缀-这给了您O(n^2)@Michael我提供了一个算法,我相信它可以解决你的O(N)问题,但你从未评论过它。你发现我的解决方案是正确的吗?如果是,请将其标记为正确答案,如果不是,那么我想听听你的意见。我猜基数树也可以用于相同数量的操作。正如Archeg所说,基数树可以用于节省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