AcWing 1927. 自动补全

542 阅读3分钟

目录:算法日记

题目来源:1927. 自动补全

题目描述

奶牛贝茜买了一个新手机,并十分喜欢用它发短信。

但是它的大蹄子在手机的小屏幕上打字时遇到了麻烦,它总是把单词拼错。

农夫约翰同意通过编写一个自动补全程序来帮助它,当它输入部分单词时,该应用程序可以给出补全建议。

自动补全程序可以访问一个包含 WW 个单词的词典,每个单词由 a..za..z 范围内的小写字母组成,其中所有单词中的字母总数最多为 10000001000000

现在给定一个包含 NN 个部分单词的列表,每个部分单词最多包含 10001000 个字母。

对于每个部分单词 ii,还会提供一个整数 KiK_i,自动补全程序需要找到词典中以部分单词 ii 作为前缀的所有单词中,按字典序排序排在第 KiK_i 个的单词。

也就是说,对所有部分单词 ii 的有效补全按字典序进行排序,输出此顺序下的第 KiK_i 个补全。

输入格式

第一行包含两个整数 WW 和 NN

接下来 WW 行,按单词在词典中的顺序,每行描述一个单词。字典中的所有字符串均互不相同。

接下来 NN 行,每行首先包含一个整数 KiK_i,然后包含一个部分单词 ii

输出格式

共 NN 行,第 ii 行输出部分单词 ii 的第 KiK_i 个有效补全在词典中出现的位置(一个 1W1∼W 之间的整数)。

如果第 KiK_i 个有效补全不存在,则输出 1−1

数据范围

  • 1W300001≤W≤30000
  • 1N10001≤N≤1000

输入样例

10 3
dab
ba
ab
daa
aa
aaa
aab
abc
ac
dadba
4 a
2 da
4 da

输出样例

3
1
-1

算法思路

  • 性质:字符串按字典序排序后,若有多个字符串的前缀相同,则这些字符串必定连续
  • 证明:按字典序排序后,有前缀相同的字符串s1 s2,设存在字符串s3s3s1 s2之间。若s3s1 s2前缀不同,则s3字典序必定小于或大于s1 s2,这与s3所在位置矛盾。 由上述性质可知,按字典序排序后,前缀相同的字符串必相邻。则字典序序列存在单调性,可使用二分。

设所求前缀为pre,则第KiK_i个有效补全为:从第一个前缀为pre的字符串开始,往后数KiK_i个。

使用二分查找第一个以pre为前缀的字符串,二分出第一个字典序大于等于前缀pre的字符串

AC代码

#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<string, int> PSI;
const int N = 3e4 + 10;
PSI dic[N];
int w, n;
int main() {
    cin>>w>>n;
    for(int i = 1; i <= w; i++) {
        string s;
        cin>>s;
        dic[i] = {s, i};
    }
    sort(dic + 1, dic + 1 + w);
    while(n--) {
        int k;
        string s;
        cin>>k>>s;
        int l = 1;
        int r = w + 1;
        while(l < r) {
            int mid = l + r >> 1;
            if(dic[mid].first >= s) r = mid;
            else l = mid + 1;
        }
        int t = l + k - 1;
        if(t > w || dic[t].first.substr(0, s.size()) != s) cout<<-1<<endl;
        else cout<<dic[t].second<<endl;
    }
    return 0;
}