各路大神用的都是啥二分,lower_bound()的
菜鸡并不想用,于是菜鸡写了一个很暴力的算法
我们先建字典树
因为是固定前缀求字典序第K大
所以我们先跑到给定前缀的最后一个字母在字典树上的位置
在以这个点为根的子树中,我们暴力查找第K个字串
具体实现过程如下
1.判断结尾在该子树内的字符串是否有k个,没有输出-1
2.不然的话从当前点开始dfs,每次跳到一个点
如果是一个字符串的结尾,排名减一
如果k等于0且当前点为字符串结尾,返回当前字符串
如果k等于0单当前点不是结束节点,那么从当前点开始按字典序最小开始遍历到第一个为字符串结尾的节点,返回该节点字符串编号。
如果k不等于0,我们按字典序从小到大遍历所有出边,如果当前出边连接的子树内结束节点的个数小于k个,就用k减去结束节点个数,如果所连接子树内结束节点数量大于等于k,我们dfs该点,return得到的值,因为保证了一定存在不小于k个字符串,所以一定能搜到
总体过程感觉和二叉搜索树找排名为k的值的流程差不多,只是换成多叉了
每个点子树内结束节点的个数,我们使用dfs预处理出来就好了
有一点点长,不像lower_bound就二十行…但是并不是很难写
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e5+7;
const int M = 1e3+7;
struct node{
int end,size;
int ch[27];
char l;
}tr[maxn*10];
int n,m,tot;
string get[maxn];
void insert(string a,int pos){
int root=0;
for(int i=0;i<a.length();i++){
if(!tr[root].ch[a[i]-'a'+1])tr[root].ch[a[i]-'a'+1]=++tot;
root=tr[root].ch[a[i]-'a'+1];
tr[root].l=a[i];
}
tr[root].end=pos;//tr[root].size=1;
}
void dfs(int now){
if(tr[now].end)tr[now].size++;
for(int i=1;i<=26;i++){
if(!tr[now].ch[i])continue;
dfs(tr[now].ch[i]);
tr[now].size+=tr[tr[now].ch[i]].size;
}
}
int find(int now,int k){
if(tr[now].end)k-=1;
if(!k&&tr[now].end)return tr[now].end;
else if(!k){
for(int i=1;i<=26;i++){
if(tr[now].ch[i]){
return find(tr[now].ch[i],k);
}
}
}
else{
for(int i=1;i<=26;i++){
if(tr[now].ch[i]){
if(k>tr[tr[now].ch[i]].size)k-=tr[tr[now].ch[i]].size;
else return find(tr[now].ch[i],k);
}
}
}
}
int query(string a,int k){
int root=0;
for(int i=0;i<a.length();i++){
if(!tr[root].ch[a[i]-'a'+1])return -1;
root=tr[root].ch[a[i]-'a'+1];
}
if(tr[root].size<k)return -1;
else return find(root,k);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
cin>>get[i];
insert(get[i],i);
}
dfs(0);
/*for(int i=0;i<=tot;i++){
cout<<tr[i].l<<" :";
for(int j=1;j<=26;j++){
if(tr[i].ch[j])cout<<tr[tr[i].ch[j]].size<<" ";
}
cout<<endl;
}*/
for(int i=1;i<=m;i++){
int x;string ch;
cin>>x>>ch;
int ret=query(ch,x);
if(ret==-1)puts("-1");
else cout<<ret<<endl;
}
return 0;
}