题意:按照出现频率由大到小给出n个单词,
操作1:输入一个字符或者删除当前字符.
操作2:根据现在所输入的字符串s,自动联想到字典中前缀为s并且出现频率最大的单词.
n,m<=1e5 总的字符数<=1e6.m次询问,每次给出一个单词,问输入该单词最少需要多少次操作.
操作次数最多为单词的长度,一次操作2会输入若干个字符 可能会减少操作次数.
建图:先对字典中的单词建立Trie.相邻节点连接双向边.并对每个节点增加一条单向边,连接到该节点为前缀时,频率最大的单词节点.
现在BFS求出到达Trie中每个节点的最小距离,也就是输入该节点最少的操作次数
查询的单词s顺着Trie走. 当不能在往下走时,说明不可能在使用到操作2,此时累加上剩余长度即可.
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
const int ch_sz=26;
struct Trie{
int ch[N][ch_sz];
int dis[N],sz,ans;
vector<int> e[N];
queue<int> q;
void init(){
for(int i=0;i<=sz;i++) e[i].clear();
sz=1,ans=1e8;
while(!q.empty())q.pop();
memset(dis,-1,sizeof(dis));
memset(ch[0],0,sizeof(ch[0]));
}
int idx(char c){
return c-'a';
}
void insert(char *s){
vector<int> v;
int u=0,n=strlen(s);
for(int i=0;s[i];i++){
int c=idx(s[i]);
if(!ch[u][c]){
memset(ch[sz],0,sizeof(ch[sz]));
e[u].push_back(sz);
e[sz].push_back(u);
if(i<n-2) v.push_back(sz);//
ch[u][c]=sz++;
}
u=ch[u][c];
}
for(int i=0;i<v.size();i++) e[v[i]].push_back(u);
}
int query(char *s){
int u=0,n=strlen(s);
ans=1e8;
for(int i=0;i<n;i++){
int c=idx(s[i]);
if(!ch[u][c]) break;
u=ch[u][c];
ans=min(ans,dis[u]+n-i-1);
}
ans=min(ans,n);
return ans;
}
void BFS(){
q.push(0),dis[0]=0;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(dis[v]==-1){
dis[v]=dis[u]+1;
q.push(v);
}
}
}
}
}trie;
int n,m;
char s[N];
int main(){
//ios::sync_with_stdio(false);cin.tie(0);
scanf("%d%d",&n,&m);
trie.init();
for(int i=1;i<=n;i++){
scanf("%s",s);
trie.insert(s);
}
trie.BFS();
while(m--){
scanf("%s",&s);
printf("%d\n",trie.query(s));
}
return 0;
}