【问题标题】:Count strings in a range计算范围内的字符串
【发布时间】:2015-03-28 18:06:48
【问题描述】:

给定编号为 1 到 N 的 N 个字符串。我们需要回答 Q 个查询。

每个查询的格式为 l r S

对于每个查询,我们需要打印字符串 S 在 [l,r] 范围内出现的次数。

例子:

让 N=3 和字符串是:

abc
def
abc

假设我们有 3 个查询:

查询 1 : 1 2 abc

那么答案是1

查询 2:1 3 abc

那么答案是2

查询 3:1 2 hgj

那么答案是0

如果 N 和 Q 都可以达到 100000 ,如何有效地回答查询。此外,输入中每个字符串的大小都大于或等于 5 且小于或等于 10。

【问题讨论】:

  • 为什么查询1的答案是1?为什么第一个和最后一个字符串不是 2 ?我错过了什么?
  • 另外,我们可以得到query = 1 3 a吗?我们是否需要查找范围内包含的子字符串?还是完全匹配?
  • @amit 完全匹配,对于查询 1,我们只需要检查字符串编号 1 和 2
  • @amit for 1 3 a type query ,答案为零
  • 知道了,以为您在寻找子字符串,而 l r 表示每个字符串中的索引。感谢澄清

标签: algorithm data-structures


【解决方案1】:

这是这个问题的标准解决方案:

  1. 让我们有三种类型的事件:查询开始、查询结束和字符串出现。它们应该按照它们在给定数组中的位置的升序进行处理。

  2. 我们还将维护一个从字符串到目前出现次数的映射。

  3. 遇到开始事件时,需要从答案中减去查询对应的字符串出现的次数。

  4. 当我们看到结束事件时,我们需要将这个字符串的出现次数添加到答案中。

  5. 当一个字符串出现时,我们只是把它放到地图中。

如果使用基于树的地图实现,时间复杂度是O((N + Q) log N),如果我们使用哈希表,时间复杂度是O(N + Q)

这是该解决方案的 C++ 实现:

struct Query {
    int left;
    int right;
    std::string str;

    Query(int left, int right, const std::string& str): 
        left(left), right(right), str(str) {}
};

// Returns the answers to the given queries. Queries' ranges
// must be 1-based.
std::vector<int> answerQueries(const std::vector<std::string>& s, 
        const std::vector<Query>& queries) {
    std::vector<std::vector<int>> startAt(s.size());
    std::vector<std::vector<int>> endAt(s.size());
    for (size_t i = 0; i < queries.size(); i++) {
        Query q = queries[i];
        startAt[q.left - 1].push_back(i);
        endAt[q.right - 1].push_back(i);
    }
    std::map<std::string, int> count;
    std::vector<int> res(queries.size());
    for (size_t i = 0; i < s.size(); i++) {
        for (size_t q : startAt[i]) {
            res[q] -= count[queries[q].str];
        }
        count[s[i]]++;
        for (size_t q : endAt[i]) {
            res[q] += count[queries[q].str];
        }
    } 
    return res;
}

【讨论】:

  • 你为什么要使用子串?需要匹配整个字符串
  • @Mrinal 我误读了这个问题。这种方式更容易。
  • @Mrinal 是的。我稍微改变了我的答案。现在应该没事了。
  • 这里的string_ids是什么??
  • @Mrinal 将所有字符串映射到数字的结果。
【解决方案2】:
  1. 为给定的字符串创建一个映射字符串 -> 在输入中排序位置
  2. 对于每个查询,使用 2 次二进制搜索,在排序位置对 l 和 r 进行查找,以查找范围内的出现次数

【讨论】:

  • 你有问题吗?
  • 好吧,如果我们需要像你说的那样匹配整个字符串,这种方法应该可行。要找到 S 在范围内出现的次数,只需减去位置 p1 - p2,其中 p1 = map[S].binary_search(l) 和 p2 = map[S].binary_search(r)。空间复杂度O(N),时间复杂度O(Q*log(N))
  • @mechatroner 你会怎么做 对于给定的字符串创建一个映射字符串 -> 在输入中排序位置??
  • @Mrinal 在 Python 中:pss = dict() for p, ln in enumerate(sys.stdin): ln = ln.rstrip('\n') if (ln not in pss): pss[ln] = list() pss[ln].append(p)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-28
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
  • 2018-05-12
  • 1970-01-01
相关资源
最近更新 更多