持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情。
题目信息
奶牛贝茜买了一个新手机,并十分喜欢用它发短信。
但是它的大蹄子在手机的小屏幕上打字时遇到了麻烦,它总是把单词拼错。
农夫约翰同意通过编写一个自动补全程序来帮助它,当它输入部分单词时,该应用程序可以给出补全建议。
自动补全程序可以访问一个包含 W 个单词的词典,每个单词由 a..z范围内的小写字母组成,其中所有单词中的字母总数最多为 1000000。
现在给定一个包含 NN 个部分单词的列表,每个部分单词最多包含 10001000 个字母。
对于每个部分单词 ii,还会提供一个整数 KiKi,自动补全程序需要找到词典中以部分单词 ii 作为前缀的所有单词中,按字典序排序排在第 KiKi 个的单词。
也就是说,对所有部分单词 ii 的有效补全按字典序进行排序,输出此顺序下的第 KiKi 个补全。
输入格式
第一行包含两个整数 WW 和 NN。
接下来 WW 行,按单词在词典中的顺序,每行描述一个单词。字典中的所有字符串均互不相同。
接下来 NN 行,每行首先包含一个整数 KiKi,然后包含一个部分单词 ii。
输出格式
共 NN 行,第 ii 行输出部分单词 ii 的第 KiKi 个有效补全在词典中出现的位置(一个 1∼W1∼W 之间的整数)。
如果第 KiKi 个有效补全不存在,则输出 −1−1。
数据范围
1≤W≤300001≤W≤30000, 1≤N≤100001≤N≤10000 1≤Ki≤W1≤Ki≤W
输入样例:
10 3
dab
ba
ab
daa
aa
aaa
aab
abc
ac
dadba
4 a
2 da
4 da
输出样例:
3
1
-1
样例解释
a 的自动补全为 aa,aaa,aab,ab,abc,ac,第四个为 ab,在词典中排第 33 个。
da 的自动补全为 daa,dab,dadba,第二个为 dab,在词典中排第 11 个。
da 不存在第四个自动补全。
思路
二分做法
- 存储单词和它在字典中的位置
- 将所有单词按字典序排序
- 对于给定前缀 prepre,用二分找到字典序大于等于它的最左位置 pp,pp 之前的单词前缀一定不是 prepre
- 检查 pp 后面第 k−1k−1 个位置上的单词 ww,如果前缀为 prepre 说明 dict[p, p+k−1]dict[p, p+k−1] 这 kk 个单词的前缀都是 prepre,输出 ww 在字典中的位置。否则,答案不存在
代码
#include <iostream>
#include <algorithm>
#define w first
#define o second
using namespace std;
typedef pair<string, int> PSI;
vector<PSI> dict;
int binary_search(string& pre) {
int l = 0, r = dict.size() - 1;
while (l < r) {
int m = l + r >> 1;
if (dict[m].w >= pre) r = m;
else l = m + 1;
}
return l;
}
int main() {
int w, n; cin >> w >> n;
for (int i = 1; i <= w; i ++) {
string s; cin >> s;
dict.push_back({s, i});
}
sort(dict.begin(), dict.end());
while (n --) {
int k; string pre;
cin >> k >> pre;
int p = binary_search(pre) + k - 1;
if (p < dict.size() && dict[p].w.substr(0, pre.size()) == pre)
cout << dict[p].o << '\n';
else
cout << -1 << '\n';
}
return 0;
}