【问题标题】:Find longest subsequence s of String in a dictionary在字典中查找String的最长子序列s
【发布时间】:2017-03-02 17:11:46
【问题描述】:

查找字符串的最长子序列s,例如“abccdde” 并给出一个字典 {"ab","add","aced"} .上面例子的结果是“add”

我在一次采访中被问到,我用特里树给出了答案,最坏的情况是 O(n*m) , n 是 s 的长度 , m 是字典的长度。 但我的平均成本应该很低。我没有通过面试,因为面试官认为我的解决方案不是最好的。有人有更好的主意吗?

【问题讨论】:

  • 您能否提供更多有关问题约束和解决方案的详细信息?如果您将 dictionary 存储在 trie 中,您的复杂度应该是 O(n*k)(其中 k 是起始字符串的长度),而不是 O(n*m)。但是,如果您只在每个字典中查询一个单词,那就太糟糕了。
  • 谢谢。我的解决方案使用特里树来存储字典。但是我为每个树节点添加了一个标签,以显示我们是否在字符串的某个前缀中具有该节点的前缀子序列。
  • 对于 "abdc" 和字典是 {"abc","adc"}。特里树是 "a->(b->c,d->c)" 。最初,所有节点的标签都设置为假。下次我用一个Hashmap来存储我们感兴趣的所有节点,原因一开始,map是{'a':"a node"},我开始遍历abcd,当找到a时,我会设置"一个节点”为 true 。将“b 节点”和“d 节点”添加到地图中,我使用动态编程来遍历字符串并更新每个节点的状态。最长的“真实”节点将是解决方案。
  • 我突然发现时间不是O(m*n),而是O(n+A),A是字符串中所有有效子序列的长度,因为我只遍历每个有效节点一次.

标签: algorithm subsequence


【解决方案1】:

您可以创建一个图形,然后顶点就是您的字母表。对于字典中的每个单词,您将在图表中添加第一个字符,例如:

G[word[0]].add({word, 0})

然后,当您访问每个字母的文本时,您会访问该字母的 adyacency 列表。对于列表中的每个项目,您应该为该单词添加下一个字符。

用你的例子:

S = "abccdde", D = {"ab","add","aced"}

第一步:

G = {{'a', [{"ab", 0}, {"add", 0}, {"aced", 0}]}}

对于 S 中的每个字符

  • 字符 = 'a' -> S[0]

您访问该角色的列表

[{"ab", 0}, {"add", 0}, {"aced", 0}]

并更新您的图表

G = {{'b', [{"ab", 1}]}, {'d', ["add", 1]}, {'c', [{"aced", 1}]}}
  • 字符'b' -> S[1]

您访问该角色的列表

[{"ab", 1}]

并更新您的图表

G = {{'d', ["add", 1]}, {'c', [{"aced", 1}]}}

完成“ab”后,您可以尝试改进答案。

  • 字符'c' -> S[2]

您访问该角色的列表

[{"aced", 1}]

并更新您的图表

G = {{'d', ["add", 1]}, {'e', [{"aced", 2}]}}
  • 字符'c' -> S[3]

没有该字符的列表,然后您继续下一个字符

  • 字符 'd' -> S[4]

您访问该角色的列表

["add", 1]

并更新您的图表

G = {{'d', ["add", 2]}, {'e', [{"aced", 2}]}}

...

【讨论】:

    【解决方案2】:

    你可以用这个方法

    public static Boolean IsSubsequence(string ch, string item)
    {
        if (ch.Length < item.Length)
        {
            return false;
        }
        int indexItem = 0;
        int indexCh = 0;
        while (indexCh < ch.Length && indexItem< item.Length)
        {
            if (ch[indexCh] == item[indexItem])
            {
                indexItem++;
            }
            indexCh++;
        }
        return indexItem == item.Length;
    }
    

    是o(n)方法 您也可以先按单词长度对字典项进行排序,这样第一个返回 true 的就是结果

    【讨论】:

    • 你确定是 O(n) 吗?我需要比较每一项,所以你的方法应该是 O((n+l)*m) m 是字典的长度,l 是字典项的长度
    • 我的意思是函数本身是 o(n),因为在 while 循环中我总是递增 indexCh,因此代码将执行 n 时间最大值(其中 n 是 ch 的长度)。因此,如果字典中有 m 个字符串,则复杂度将为 O(n*m)
    【解决方案3】:

    安排您的字典数据结构,以便对于每个有效字母,您会更深入地进入树(查看单词长一个字母),并添加一个默认大小写(没有与此前缀匹配的字母)指向最长的有效前缀比当前深度短(还有一个标志,表明你已经有一个词,以防万一结果是你的最佳选择)。

    当您错过“asparag”后跟“r”时,您的字典会将您定向到“sparag”的树如果有任何这样的词,如果没有,那么它会指引您到“段落”。

    对于每次失败,如果仍然没有匹配项,则重复测试并递归到更短的单词;所以这仍然比 O(n) 更糟……尽管稍作思考表明最坏的情况可能是 O(2n)。

    为了加快速度,默认值可以是默认值的列表,您可以从中选择与当前字母匹配的条目。所有条目将至少匹配长度为 0(当前字母无词开头)或 1(仅当前字母)的条目。

    【讨论】:

      【解决方案4】:

      这是我的 Python 代码,用于时间复杂度为 O(N + L) 的解决方案,其中 N 是字符串中的字符数(“abccdde”为 N = 7),L 是字符串中的字符总数字典(L = 9 表示 {"ab","add","aced"})。基本上是线性时间复杂度。

      def find_longest_word_in_string(string, words):
          m = {}
      
          for word in words:
              m[word] = 0
      
          for c in string:
              for word in m.keys():
                  if len(word) == m[word]:
                      continue
                  else:
                      if word[m[word]] == c:
                          m[word] += 1
      
          length = 0
          for word in m.keys():
              if m[word] == len(word) and m[word] > length:
                  res = word
                  length = m[word]
      
          return res
      
      if __name__ == '__main__':
         s = "abccdde"                 
         words = ["ab","add","aced"]    
         print find_longest_word_in_string(s, words)
      

      运行它,返回“添加”

      【讨论】:

      • 您使用了两个 for 循环,这不是 O(n^2) 解决方案吗?
      【解决方案5】:

      我认为不可能只使用一个循环来减少算法所花费的时间,至少它需要两个循环我猜,这是我的方法:

      public String lookFor(String inputWord, String[] dictionary) {
      
        Arrays.sort(dictionary);
      
        for (int index = dictionary.length - 1; index > 0; index--) {
          if (isTheWordASubsequence(inputWord, dictionary[index]))
            return dictionary[index];
        }
      
        return null;
      }
      
      private boolean isTheWordASubsequence(String inputWord,
          String dictionaryWord) {
      
        int spot = 0;
        int offset = 0;
      
        for (char item : dictionaryWord.toCharArray()) {
          spot = (offset = inputWord.indexOf(item, spot)) >= spot ? offset : -1;
          if (spot < 0)
            return false;
        }
      
        return true;
      }
      

      【讨论】:

        猜你喜欢
        • 2012-10-15
        • 1970-01-01
        • 1970-01-01
        • 2018-09-10
        • 2014-03-17
        • 1970-01-01
        • 1970-01-01
        • 2016-09-13
        • 2011-03-20
        相关资源
        最近更新 更多