【问题标题】:Google Coding Challenge Question 2020 : Unspecified Words2020 年谷歌编码挑战题:未指明的单词
【发布时间】:2020-12-07 20:20:29
【问题描述】:


我在 2020 年 8 月 16 日举行的 Google 编码挑战赛中遇到了以下问题。我试图解决它,但无法解决。

字典中有N 单词,每个单词都是固定的 长度和M 仅由小写英文字母组成,即 ('a', 'b', ...,'z')
查询词由Q 表示。长度 查询词是M。这些单词包含小写英文字母 但在某些地方而不是'a', 'b', ...,'z'之间的字母 有'?'。请参阅示例输入部分以了解这一点 案子。

Q 的匹配计数,用 match_count(Q) 表示是 字典中包含相同英语的单词数 中的字母(不包括可以在?位置的字母) 与查询词Q 中的字母位置相同。其他 单词,字典中的单词可以包含任何字母 '?' 的位置,但其余字母必须与 查询词。

给你一个查询词 Q,你需要计算 match_count.

输入格式

  • 第一行包含两个空格分隔的整数NM,分别表示字典中的单词数和每个单词的长度 分别。
  • 接下来的N 行各包含一个字典中的单词。
  • 下一行包含一个整数 Q,表示您必须计算 match_count 的查询词的数量。
  • 接下来的Q 行每行包含一个查询词。

输出格式
对于每个查询词,打印match_count 以获得新行中的特定词。

约束

1 <= N <= 5X10^4
1 <= M <= 7 
1 <= Q <= 10^5


所以,我有 30 分钟的时间来回答这个问题,我可以编写以下不正确的代码,因此没有给出预期的输出。

def Solve(N, M, Words, Q, Query):
    output = []
    count = 0
    for i in range(Q):
        x = Query[i].split('?')
        for k in range(N):
            if x in Words:
               count += 1
            else:
                pass
        output.append(count)
    return output

N, M = map(int , input().split())
Words = []
for _ in range(N):
    Words.append(input())

Q = int(input())
Query = []
for _ in range(Q):
    Query.append(input())

out =  Solve(N, M, Words, Q, Query)
for x in out_:
    print(x)

有人可以帮我提供一些可以解决这个问题的伪代码或算法吗?

【问题讨论】:

  • 显而易见的算法难道不是“对于每个 Query,对于每个 Word,对于 Query 中的每个字母,如果字母相同或问号,则 count+=1”?跨度>
  • 我正在努力考虑对不需要大量内存的明显算法进行改进。到目前为止,我有 N+2^M 内存用于预计算位域,还有 N+M!预计算尝试的内存。
  • 是的,count+=1!!但我得记下这封信的位置。此外,多个“?”可以在查询中出现。
  • 一个人可以使用 N*M 额外的内存来使明显的查询快约 26 倍,我想,通过每个单词只跟踪一个字母而不是完整的位域......
  • "多个 ? 可以在一个查询中出现" 如果您一次比较每个字母,这不是问题。不要使用拆分。

标签: python string algorithm


【解决方案1】:

我想我的第一次尝试是将查询中的? 替换为.,即将?at 更改为.at,然后将它们用作正则表达式并将它们与所有单词进行匹配在字典中,就像这样简单:

import re
for q in queries:
    p = re.compile(q.replace("?", "."))
    print(sum(1 for w in words if p.match(w)))

但是,将输入大小视为 N 最大为 5x104 和 Q 最大为 105,这可能太慢了,就像比较所有对的任何其他算法一样单词和查询。

另一方面,请注意M,每个单词的字母数,是恒定的并且相当低。因此,您可以为所有位置的所有字母创建 Mx26 组单词,然后获取这些组的交集。

from collections import defaultdict
from functools import reduce

M = 3
words = ["cat", "map", "bat", "man", "pen"]
queries = ["?at", "ma?", "?a?", "??n"]

sets = defaultdict(set)
for word in words:
    for i, c in enumerate(word):
        sets[i,c].add(word)

all_words = set(words)
for q in queries:
    possible_words = (sets[i,c] for i, c in enumerate(q) if c != "?")
    w = reduce(set.intersection, possible_words, all_words)
    print(q, len(w), w)

在最坏的情况下(一个查询有一个非? 字母对字典中的大多数或所有单词都是通用的)这可能仍然很慢,但在过滤单词时应该比迭代所有单词要快得多每个查询的单词。 (假设单词和查询中的字母都是随机的,第一个字母的单词集合将包含 N/26 个单词,前两个的交集包含 N/26² 个单词,等等)

考虑到不同的情况,这可能会有所改善,例如(a) 如果查询不包含任何?,只需检查它是否在单词的set (!) 中,而不创建所有这些交集; (b) 如果查询为all-?,则返回所有单词的集合; (c) 对可能的词集按大小排序,首先从最小的集合开始交集,以减少临时创建的集合的大小。

关于时间复杂度:说实话,我不确定这个算法的时间复杂度是多少。 N、Q 和 M 分别是词数、查询数以及词和查询的​​长度,创建初始集的复杂度为 O(N*M)。之后,查询的复杂性显然取决于查询中非? 的数量(以及要创建的集合交集的数量),以及集合的平均大小。对于具有零个、一个或 M 个非? 字符的查询,查询将在 O(M) 中执行(评估情况,然后进行单个 set/dict 查找),但对于具有两个或多个非@987654334 的查询@-characters,第一组交叉点的平均复杂度为 O(N/26),严格来说仍然是 O(N)。 (以下所有交叉点只需要考虑 N/26²、N/26³ 等元素,因此可以忽略不计。)我不知道这与 Trie 方法相比如何,如果有其他答案可以详细说明,我会非常感兴趣关于那个。

【讨论】:

  • 我也为正则表达式方法添加了代码,但我怀疑这会在时间限制内执行。
  • 我从来没有在python中使用过reduce和defaultdict...会尝试通过它..但它工作顺利..谢谢!
  • 该算法的时间复杂度与 trie 方法相比如何?
  • @Alireza 由于我没有看到单一的“the” Trie 方法,但只有几个答案说使用 Trie,然后在没有任何解释的情况下丢弃大量代码,你最好问问他们。
【解决方案2】:

这个问题可以借助 Trie 数据结构来完成。 首先将所有单词添加到trie ds。 然后你必须看看这个词是否出现在trie中,有一个特殊的条件'?'所以你也必须注意这种情况,比如角色是?然后只需转到单词的下一个字符。

我认为这种方法可行,Leetcode 中有一个类似的问题。

链接:https://leetcode.com/problems/design-add-and-search-words-data-structure/

【讨论】:

    【解决方案3】:

    它应该是 O(N) 的时间和空间方法,因为 M 很小并且可以被认为是恒定的。您可能想在此处查看 Trie 的实现。

    执行第一遍并将单词存储在 Trie DS 中。

    接下来,对于您的查询,您按以下顺序执行 DFS 和 BFS 的组合。

    如果你收到一个?,执行 BFS 并添加所有的孩子。 对于非 ?,执行 DFS 并且应该指向一个单词的存在。

    为了进一步优化,也可以使用后缀树来存储DS。

    【讨论】:

      【解决方案4】:

      您可以使用简化版本的 trie,因为查询字符串具有预定义的长度。 Trie 节点中不需要ends 变量

      #include <bits/stdc++.h>
      using namespace std;
      
      typedef struct TrieNode_ {
          struct TrieNode_* nxt[26];
      } TrieNode;
      
      void addWord(TrieNode* root, string s) {
          TrieNode* node = root;
          for(int i = 0; i < s.size(); ++i) {
              if(node->nxt[s[i] - 'a'] == NULL) {
                  node->nxt[s[i] - 'a'] = new TrieNode;
              }
              node = node->nxt[s[i] - 'a'];
          }
      }
      
      void matchCount(TrieNode* root, string s, int& cnt) {
          if(root == NULL) {
              return;
          }
          if(s.empty()) {
              ++cnt;
              return;
          }
          TrieNode* node = root;
          if(s[0] == '?') {
              for(int i = 0; i < 26; ++i) {
                  matchCount(node->nxt[i], s.substr(1), cnt);
              }
          }
          else {
              matchCount(node->nxt[s[0] - 'a'], s.substr(1), cnt);
          }
      }
      
      int main() {
          int N, M;
          cin >> N >> M;
          vector<string> s(N);
          TrieNode *root = new TrieNode;
          for (int i = 0; i < N; ++i) {
              cin >> s[i];
              addWord(root, s[i]);
          }
          int Q;
          cin >> Q;
          for(int i = 0; i < Q; ++i) {
              string queryString;
              int cnt = 0;
              cin >> queryString;
              matchCount(root, queryString, cnt);
              cout << cnt << endl;
          }
      }
      

      【讨论】:

      • 您能否详细说明一下这是如何工作的?如果我正确理解代码,当遇到 ? 时,您将检查 Trie 的所有分支。但这意味着,对于像 ????x 这样的查询,您基本上会探索整个 Trie,对吧?我可能遗漏了一些东西,但我认为 Set-approach 有点像 Trie,除了它可以直接潜入 x(或通常是最受限制的字符)并忽略所有 ?,对吗? ?
      • @tobias_k 我认为你是对的。在最坏的情况下,查询像?????????可能需要 26^7 = 8*10^9 的时间复杂度。这只是一个这样的查询。对于 Q 查询,这是不可行的
      • @tobias_k 代码完全符合您的描述。在我们得到? 之前,set 方法确实就像是对最大前缀的尝试。 Trie 方法适用于小型 M。它还节省了查找集合交点的开销。 PS:现在心疼我没有在python中练习cp:/.
      【解决方案5】:

      注意事项: 1. 此代码不读取输入,而是从 main 方法中获取参数。 2. 对于较大的输入,我们可以使用 java 8 流来并行化搜索过程并提高性能。

      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      public class WordSearch {
      
      private void matchCount(int N, int M, int Q, String[] words,  String[] queries) {
          
          Pattern p = null;
          Matcher m = null;
          int count = 0;
          
          for (int i=0; i<Q; i++) {
              
              p = Pattern.compile(queries[i].replace('?','.'));
              for (int j=0; j<N; j++) {
                  m = p.matcher(words[j]);
                  if (m.find()) {
                      count++;    
                  }
              }
              System.out.println("For query word '"+ queries[i] + "', the count is: " + count) ;
              count=0;
          }
          System.out.println("\n");
          
      }
      
      
      public static void main(String[] args) {
          
          WordSearch ws = new WordSearch();
          int N = 5; int M=3; int Q=4;
          String[] w = new String[] {"cat", "map", "bat", "man", "pen"};
          String[] q = new String[] {"?at", "ma?", "?a?", "??n" };
          ws.matchCount(N, M, Q, w, q); 
          
          w = new String[] {"uqqur", "1xzev", "ydfgz"}; 
          q = new String[] {"?z???", "???i?", "???e?", "???f?", "?z???"};
          N=3; M=5; Q=5;
          ws.matchCount(N, M, Q, w, q);
          
      }
      

      }

      【讨论】:

        【解决方案6】:

        我可以想到一种带有 bfs 的 trie 查找方法

        class Node:
        
        def __init__(self, letter):
            self.letter = letter
            self.chidren = {}
        
        @classmethod
        def construct(cls):
            return cls(letter=None)
        
        def add_word(self, word):
            current = self
        
            for letter in word:
                if letter not in current.chidren:
                    node = Node(letter)
                    current.chidren[letter] = node
                else:
                    node = current.chidren[letter]
                current = node
        
        def lookup_word(self, word, m):
            def _lookup_next_letter(_letter, _node):
                if _letter == '?':
                    for node in _node.chidren.values():
                        q.put((node, i))
        
                elif _letter in _node.chidren:
                    q.put((_node.chidren[_letter], i))
        
            q = SimpleQueue()
            count = 0
            i = 0
            current = self
        
            letter = word[i]
            i += 1
        
            _lookup_next_letter(letter, current)
        
            while not q.empty():
                current, i = q.get()
                if i == m:
                    count += 1
                    continue
        
                letter = word[i]
                i += 1
                _lookup_next_letter(letter, current)
        
            return count
        
        def __eq__(self, other):
            return self.letter == other.letter if isinstance(other, Node) else other
        
        def __hash__(self):
            return hash(self.letter)
        

        【讨论】:

          【解决方案7】:

          我会为每个单词的每个字母创建一个查找表,然后使用该表进行迭代。虽然查找表将花费 O(NM) 内存(或在所示情况下为 15 个条目),但它允许实现简单的 O(NM) 时间复杂度,最佳情况为 O(log N * log M)。

          查找表可以以坐标平面的形式存储。每个字母都有一个“x”位置(字母索引)和一个“y”位置(字典中的单词索引)。这将允许从查询中快速交叉引用,以查找字母的存在位置和单词的位置是否合格。

          最坏的情况,这种方法的时间复杂度为 O(NM),因此必须有 N 次迭代,每个字典条目一个,M 次迭代,每个条目中的每个字母一个。但在许多情况下,它会跳过查找。

          还创建了一个坐标系,其空间复杂度也为 O(NM)。

          不熟悉 python,所以这是用 JavaScript 编写的,在语言方面我尽可能接近。希望这至少可以作为一个可能的解决方案的示例。

          此外,作为附加部分,我包括了一个负载较重的部分,用于性能比较。完成一个包含 2000 个单词、5000 个查询的集合大约需要 5 秒,每个查询的长度为 200。

          // Main function running the analysis
          function run(dict, qs) {
          
            // Use a coordinate system for tracking the letter and position
            var coordinates = 'abcdefghijklmnopqrstuvwxyz'.split('').reduce((p, c) => (p[c] = {}, p), {});
          
            // Populate the system
            for (var i = 0; i < dict.length; i++) {
          
              // Current word in the given dictionary
              var dword = dict[i];
          
              // Iterate the letters for tracking
              for (var j = 0; j < dword.length; j++) {
          
                // Current letter in our current word
                var letter = dword[j];
          
                // Make sure that there is object existence for assignment
                coordinates[letter][j] = coordinates[letter][j] || {};
          
                // Note the letter's coordinate by storing its array 
                // position (i) as well as its letter position (j)
                coordinates[letter][j][i] = 1;
              }
            }
          
            // Lookup the word letter by letter in our coordinate system
            function match_count(Q) {
          
              // Create an array which maps from the dictionary indices 
              // to a truthy value of 1 for tracking successful matches
              var availLookup = dict.reduce((p,_,i) => (p[i]=1,p),{});
          
              // Iterate the letters of Q to check against the coordinate system
              for (var i = 0; i < Q.length; i++) {
          
                // Current letter in Q
                var letter = Q[i];
          
                // Skip '?' characters
                if (letter == '?') continue;
          
                // Look up the existence of "points" in our coordinate system for
                // the current letter
                var points = coordinates[letter];
          
                // If nothing from the dictionary matches in this position,
                // then there are no matches anywhere and we return a 0
                if (!points || !points[i]) return 0;
          
                // Iterate the availability truth table made earlier
                // and look up whether any points in our coordinate system
                // are present for the current letter. If they are, then the word
                // remains, if not, it is removed from consideration.
                for(var n in availLookup){
                 if(!points[i][n]) delete availLookup[n];
                }
              }
          
              // Sum the "truthy" 1 values we used earlier to determine the count of
              // matched words
              return Object.values(availLookup).reduce((x, y) => x + y, 0);
            }
          
            var matches = [];
            for (var i = 0; i < qs.length; i++) {
              matches.push(match_count(qs[i]));
            }
            return matches;
          }
          
          document.querySelector('button').onclick=_=>{
          console.clear();
          var d1 = [
            'cat',
            'map',
            'bat',
            'man',
            'pen'
          ];
          var q1 = [
            '?at',
            'ma?',
            '?a?',
            '??n'
          ];
          console.log('running...');
          console.log(run(d1, q1));
          
          var d2 = [
            'uqqur', 
            'lxzev', 
            'ydfgz'
          ];
          var q2 = [
            '?z???', 
            '???i?', 
            '???e?', 
            '???f?', 
            '?z???'
          ];
          console.log('running...');
          console.log(run(d2, q2));
          
          
          // Load it up (try this with other versions to compare with efficiency)
          var d3 = [];
          var q3 = [];
          var wordcount = 2000;
          var querycount = 5000;
          var len = 200;
          
          var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
          for(var i = 0; i < wordcount; i++){
           var word = "";
           for(var n = 0; n < len; n++){
            var rand = (Math.random()*25)|0;
            word += alphabet[rand];
           }
           d3.push(word);
          }
          for(var i = 0; i < querycount; i++){
           var qword = d3[(Math.random()*(wordcount-1))|0];
           var query = "";
           for(var n = 0; n < len; n++){
            var rand = (Math.random()*100)|0;
            if(rand > 98){ word += alphabet[(Math.random()*25)|0]; }
            else{ query += rand > 75 ? qword[n] : '?'; }
           }
           q3.push(query);
          }
          
          if(document.querySelector('input').checked){
           //console.log(d3,q3);
           console.log('running...');
           console.log(run(d3, q3).reduce((x, y) => x + y, 0) + ' matches');
          }
          };
          <input type=checkbox>Include the ~5 second larger version<br>
          <button type=button>run</button>

          【讨论】:

            【解决方案8】:

            我不懂 Python,但朴素算法的要点如下:

            #count how many words in Words list match a single query 
            def DoQuery(Words, OneQuery):
                count = 0
                #for each word in the Words list
                for i in range(Words.size()):
                    word = Words.at(i)
                    #compare each letter to the query
                    match = true
                    for j in range(word.size()):
                        wordLetter = word.at(j)
                        queryLetter = OneQuery.at(j)
                        #if the letters do not match and are not ?, then skip to next word
                        if queryLetter != '?' and queryLetter != wordLetter:
                            match = false
                            break
                    #if we did not skip, the words match. Increase the count
                    if match == true
                        count = count + 1
                #we have now checked all the words, return the count
                return count
            

            当然,这会执行大约 3.5x10^10 次最里面的循环,这可能太慢了。所以需要先查字典,预先计算一些缺少的快捷方式数据结构,然后使用快捷方式更快地找到答案。

            一种快捷的数据结构是将可能的查询映射到答案,使查询 O(1)。只有 4.47*10^9 可能的查询,所以这可能更快。

            类似的快捷数据结构是尝试对可能的答案进行查询,使查询 O(M)。只有 4.47*10^9 可能的查询,所以这可能更快。这是更复杂的代码,但对某些人来说也更容易理解。

            另一个捷径是“假设”每个查询都只有一个非问号,并将可能的查询映射到子字典。这意味着您仍然必须在子集字典上运行天真的查询,但它会小约 26 倍,因此快约 26 倍。您还必须将真正的查询转换为只有一个非问号才能在地图中查找子集字典,但这应该很容易。

            【讨论】:

            • 考虑到可能的输入大小,内部if 最多可能执行3.5x10^10 次,这意味着程序可能无法在可接受的时间内完成。
            • @trincot:这绝对正确,但如果 OP 甚至无法计算出简单的算法,那么优化可能超出了他们的范围。
            • 我将描述优化思想。
            • @trincot:当 N 和 27^M 如此接近时,我很难想出好的优化方法。
            • 我们需要函数返回一个列表
            【解决方案9】:

            我认为我们可以使用 trie 来解决这个问题。 最初,我们只是将所有字符串添加到 trie 中,然后当我们得到每个查询时,我们可以检查它是否存在于 trie 中。

            这里唯一不同的是“?”但我们可以将其用作全字符匹配,因此无论何时我们都会检测到“?”在我们的搜索字符串中,我们将从这里查看所有可能的单词,然后通过在所有可能的路径中搜索单词来简单地执行 dfs。

            下面是 C++ 代码

            class Trie {
            public:
            bool isEnd;
            vector<Trie*> children;
            Trie() {
                this->isEnd = false;
                this->children = vector<Trie*>(26, nullptr);
            }
            };
            Trie* root;
            
            void insert(string& str) {
                int n = str.size(), idx, i = 0;
                Trie* node = root;
                while(i < n) {
                    idx = str[i++] - 'a';
                    if (node->children[idx] == nullptr) {
                        node->children[idx] = new Trie();
                    }
                    node = node->children[idx];
                }
                node->isEnd = true;
            }
            
            int getMatches(int i, string& str, Trie* node) {
                int idx, n = str.size();
                while(i < n) {
                    if (str[i] >= 'a' && str[i] <='z')
                        idx = str[i] - 'a';
                    else {
                        int res = 0;
                        for(int j = 0;j<26;j++) {
                            if (node->children[j] != nullptr)
                                res += getMatches(i+1, str, node->children[j]);
                        }
                        return res;
                     }
                    
                    if (node->children[idx] == nullptr) return 0;
                    node = node->children[idx];
                    ++i;
                }
                return node->isEnd ? 1 : 0;
            }
            
            int main() {
                int n, m;
                cin>>n>>m;
                string str;
                root = new Trie();
                while(n--) {
                    cin>>str;
                    insert(str);
                }
                int q;
                cin>>q;
                while(q--) {
                    cin>>str;
                    cout<<(str.size() == m ? getMatches(0, str, root) : 0)<<"\n";
                }
            }
            

            【讨论】:

              【解决方案10】:

              我可以使用以下 ascii 值吗:

              • 对于查询词中的字符计算 ascii 值总和。
              • 对于字典中的单词,按字符计算单词的 ascii 并使用查询词的 ascii 总和进行检查,例如 bat,如果 b 的 ascii 与查询词的 ascii 总和相匹配,则增加计数,否则计算 a 的 ascii 并使用查询 ascii 进行检查如果没有,则将其添加到 b 的 ascii 中,然后检查并因此最终返回计数。 这个方法怎么样?

              【讨论】:

                【解决方案11】:

                使用 Trie 实现 Java

                import java.util.*;
                import java.io.*;
                import java.lang.*;
                
                public class Main {
                
                    static class TrieNode 
                    {
                        TrieNode []children = new TrieNode[26];
                        boolean endOfWord;
                        TrieNode() 
                        { 
                            this.endOfWord = false; 
                            for (int i = 0; i < 26; i++) { 
                                this.children[i] = null; 
                            } 
                        }
                
                        void addWord(String word) 
                        { 
                            // Crawl pointer points the object 
                            // in reference 
                            TrieNode pCrawl = this; 
                    
                            // Traverse the given array of words 
                            for (int i = 0; i < word.length(); i++) { 
                                int index = word.charAt(i) - 'a'; 
                                if (pCrawl.children[index]==null) 
                                    pCrawl.children[index] 
                                        = new TrieNode(); 
                    
                                pCrawl = pCrawl.children[index]; 
                            } 
                            pCrawl.endOfWord = true; 
                        }
                        public static int ans2 = 0;
                        void search(String word, boolean found, String curr_found, int pos) 
                        { 
                            TrieNode pCrawl = this; 
                    
                            if (pos == word.length()) { 
                                if (pCrawl.endOfWord) { 
                                    
                                    found = true; 
                                    ans2++;
                                } 
                                return; 
                            } 
                    
                            if (word.charAt(pos) == '?') { 
                    
                                // Iterate over every letter and 
                                // proceed further by replacing 
                                // the character in place of '.' 
                                for (int i = 0; i < 26; i++) { 
                                    if (pCrawl.children[i] != null) { 
                                     pCrawl.children[i].search(word,found,curr_found + (char)('a' + i),pos + 1); 
                                    } 
                                } 
                            } 
                            else {  // Check if pointer at character 
                                // position is available, 
                                // then proceed 
                                if (pCrawl.children[word.charAt(pos) - 'a'] != null) { 
                                    pCrawl.children[word.charAt(pos) - 'a'] 
                                        .search(word,found,curr_found + word.charAt(pos),pos + 1); 
                                } 
                            } 
                            return; 
                        } 
                    
                        // Utility function for search operation 
                        int searchUtil(String word) 
                        { 
                            TrieNode pCrawl = this; 
                    
                            boolean found = false; 
                            ans2 = 0;
                            pCrawl.search(word, found,"",0); 
                            return ans2;
                        }   
                    }
                
                    static int searchPattern(String arr[], int N,String str) 
                    { 
                        // Object of the class Trie 
                        TrieNode obj = new TrieNode(); 
                    
                        for (int i = 0; i < N; i++) { 
                            obj.addWord(arr[i]); 
                        } 
                    
                        // Search pattern 
                        return obj.searchUtil(str); 
                    } 
                
                    
                
                    public static void ans(String []arr , int n, int m,String [] query, int q){
                        
                
                        for(int i=0;i<q;i++)
                        System.out.println(searchPattern(arr,n,query[i]));
                
                
                    }
                
                
                
                
                    public static void main(String args[]) {
                        Scanner scn = new Scanner();
                        
                            int n = scn.nextInt();
                            int m = scn.nextInt();
                            String []arr = new String[n];
                
                            for(int i=0;i<n;i++){
                                arr[i] = scn.next();
                            }
                            int q = scn.nextInt();
                
                            String []query = new String[q];
                
                            for(int i=0;i<q;i++){
                                query[i] = scn.next();
                            }
                
                            ans(arr,n,m,query,q);
                
                        
                    }
                }
                

                【讨论】:

                  【解决方案12】:

                  这很粗鲁,但 Trie 是一个更好的实现。

                  """
                  Input: db whic is a list of words
                  chk :  str to find
                  """
                  
                  def check(db,chk):
                      
                      seen = collections.defaultdict(list)
                      for i in db:
                          for j in range(len(i)):
                              temp = i[:j] + "?" + i[j+1:]
                              seen[temp].append(i)
                              
                      return len(seen[chk])
                      
                  print check(["cat","bat"], "?at")
                  

                  【讨论】:

                    【解决方案13】:

                    听起来像是https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff 的编码挑战

                    根据参数 N、M、Q 以及数据和查询分布,“最佳”算法会有所不同。一个简单的例子,给定查询???,你知道答案——字典的长度——无需任何计算?

                    在一般情况下,最有可能的是提前创建搜索索引(即在阅读字典时,在看到任何查询之前)。

                    我会这样做:给输入编号0 cat; 1 map; ...

                    然后为每个字母位置建立一个搜索索引:

                    index = [
                      {"c": 0b00001, "m": 0b00010, ...}  # first query letter
                      {"a": 0b01111, "e": 0x10000}       # second query letter
                    ]
                    

                    all = 0x11111(所有位设置)准备为“匹配所有内容”。

                    然后查询查找:?a?all &amp; index[1]["a"] &amp; all。 †

                    之后,您需要计算结果中设置的位数。

                    因此,单个查询的时间复杂度为O(N) * (M + O(1))‡,这是一个不错的权衡。

                    整批都是O(N*M*Q)

                    Python(以及 es2020)支持本机任意精度整数,可以优雅地用于位图,以及本机字典,使用它们:) 但是如果数据是稀疏的,自适应或压缩位图,例如 @987654322 @ 可能表现更好。

                    † 在实践中... &amp; index[1].get("a", 0) &amp; ... 以防你打了一个空白。

                    ‡ Python 数据结构的时间复杂度报告为 O(...)摊销最坏情况,而在 CS O(...)最坏情况中通常被考虑。虽然差异是微妙的,但它甚至可能会咬到经验丰富的开发人员,参见例如https://bugs.python.org/issue13703

                    【讨论】:

                      【解决方案14】:

                      一种方法是使用 Python 的 fnmatch 模块(对每个模式求和匹配的单词):

                      import fnmatch
                      
                      names = ['uqqur', 'lxzev', 'ydfgs']
                      patterns = ['?z???', '???i?', '???e?', '???f?', '?z???']
                      [sum(fnmatch.fnmatch(name, pattern) for name in names) for pattern in patterns]
                      
                      # [0, 0, 1, 0, 0]
                      

                      【讨论】:

                        猜你喜欢
                        • 2022-06-14
                        • 1970-01-01
                        • 2017-08-14
                        • 1970-01-01
                        • 2018-01-21
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-12-25
                        相关资源
                        最近更新 更多