查询字符串

查询字符串

给定 n 个字符串 f1,f2,,fn

这些字符串两两不同。

下面给定 q 个询问。

其中,第 i 次询问给定一个字符串 si,你的任务是:

  1. 计算 f1fnn 个字符串中,包含 si 作为子串的字符串的数量。
  2. f1fnn 个字符串中,任选一个包含 si 作为子串的字符串输出。

输入格式

第一行包含整数 n

接下来 n 行,其中第 i 行包含字符串 fi

再一行包含整数 q

接下来 q 行,其中第 i 行包含字符串 si

所有 fisi 都只包含小写字母、数字以及 . 。

输出格式

q 行,其中第 i 行输出第 i 个询问的答案。

首先输出 f1fnn 个字符串中包含 si 作为子串的字符串的数量。

然后从 f1fnn 个字符串中任选一个包含 si 作为子串的字符串输出。

如果这样的字符串不唯一,则输出任意合理字符串均可,如果这样的字符串不存在,则输出 - 。

数据范围

前三个测试点满足 1n,q20
所有测试点满足 1n100001q500001|fi|,|si|8

输入样例:

复制代码
4
test
contests
test.
.test
6
ts
.
st.
.test
contes.
st
复制代码

输出样例:

1 contests
2 .test
1 test.
1 .test
0 -
4 test.

 

解题思路

  这题并没有很复杂,突破口是字符串长度的数据范围,每个字符串最大的出度不超过8。因为题目查询的是某个子串的出现次数,所以我们可以想到,因为每个字符串的长度很小,因此字符串对应的字串数量也很少(假设字符串的长度为n,那么这个字符串包含的子串个数为1+2++n=n×(1+n)2,如果n=8,最多也只有36个子串)。我们可以把每个字符串的子串存下来,查询的时候在这些子串中查找。

  因此我们可以开两个哈希表,一个用来统计对于某个子串有多少个字符串包含这个子串,另一个用来记录某个子串对应哪个字符串(任意一个包含这个子串的字符串)。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <unordered_map>
 3 #include <unordered_set>
 4 #include <string>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 unordered_map<string, int> cnt;
 9 unordered_map<string, string> mp;
10 
11 int main() {
12     int n;
13     scanf("%d", &n);
14     for (int i = 0; i < n; i++) {
15         char s[10];
16         scanf("%s", s);
17         
18         string str = s;
19         unordered_set<string> st;   // 用来判重,如果某个字符串包含多个相同的子串,只记录一次。例如:aaaaa
20         for (int len = 0; s[len]; len++) {
21             for (int i = 0; s[i + len]; i++) {
22                 string t = str.substr(i, len + 1);
23                 if (!st.count(t)) {
24                     cnt[t]++;
25                     mp[t] = str;
26                     st.insert(t);
27                 }
28             }
29         }
30     }
31     
32     int m;
33     scanf("%d", &m);
34     while (m--) {
35         char s[10];
36         scanf("%s", s);
37         printf("%d %s\n", cnt[s], mp.count(s) ? mp[s].c_str() : "-");
38     }
39     
40     return 0;
41 }
复制代码

  因为是查找字符串,我们也可以用trie把所有的子串存下来,然后进行查找。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int N = 5e5 + 10, M = 1 << 8; // 粗略计算,一个字符串的所有子串最多会用到50个字符
 7 
 8 int trie[N][M], cnt[N], idx;
 9 char mp[N][10];
10 
11 void insert(char *s, char *str) {
12     int p = 0;
13     for (int i = 0; s[i]; i++) {
14         int t = s[i];
15         if (trie[p][t] == 0) trie[p][t] = ++idx;
16         p = trie[p][t];
17     }
18     
19     cnt[p]++;
20     strcpy(mp[p], str);
21 }
22 
23 int query(string s) {
24     int p = 0;
25     for (int i = 0; s[i]; i++) {
26         int t = s[i];
27         if (trie[p][t] == 0) return 0;
28         p = trie[p][t];
29     }
30     
31     return p;
32 }
33 
34 int main() {
35     int n;
36     scanf("%d", &n);
37     while (n--) {
38         char str[10];
39         scanf("%s", str);
40         
41         for (int i = 0; str[i]; i++) {
42             for (int j = 0; j <= i; j++) {
43                 char s[10] = {0};   // 记得初始化0,memcpy函数后面不会补0
44                 memcpy(s, str + j, i - j + 1);
45                 if (strcmp(mp[query(s)], str)) insert(s, str);  // 对于当前的字符串,如果某个子串出现过,那么就不可以再统计这个字符串了
46             }
47         }
48     }
49     
50     int m;
51     scanf("%d", &m);
52     while (m--) {
53         char str[10];
54         scanf("%s", str);
55         
56         int t = query(str);
57         if (cnt[t]) printf("%d %s\n", cnt[t], mp[t]);
58         else printf("0 -\n");
59     }
60     
61     return 0;
62 }
复制代码

 

参考资料

  AcWing 4398. 查询字符串(AcWing杯 - 周赛):https://www.acwing.com/video/3789/

posted @   onlyblues  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
历史上的今天:
2021-04-13 Saving James Bond - Hard Version
Web Analytics
点击右上角即可分享
微信分享提示