题意:先给你n个字符串,然后q次询问,每次询问以给定字符串为前缀的存在与n个字符串中的最短的单词。若有多个输出字典序最小的那个。
思路:字典树中存两个变量,一个记录该前缀的最短字符串长度,一个记录该节点是否是某个字符串的结尾。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 27;
const int INF = 0x3f3f3f3f;
const int maxnode = 20000*20+5;
int ch[maxnode][maxn], val[maxnode], l[maxnode], sz;
char str[maxn];
void init()
{
sz = 1;
memset(l, INF, sizeof(l)); //记录长度
memset(ch[0], 0, sizeof(ch[0]));
}
void Insert(char *s)
{
int u = 0;
int len = strlen(s);
for(int i = 0; i < len; i++)
{
if(ch[u][s[i]-'a'] == 0)
{
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][s[i]-'a'] = sz++;
}
u = ch[u][s[i]-'a'];
l[u] = min(len, l[u]); //维护最短长度
}
val[u] = 1; //标识结尾
}
void Match(char *s)
{
int u = 0;
int len = strlen(s);
for(int i = 0; i < len; i++)
{
if(ch[u][s[i]-'a'] == 0)
{
puts("-1"); //没有该前缀
return ;
}
u = ch[u][s[i]-'a'];
}
printf("%s", s);
while(!val[u])
{
for(int i = 0; i < 26; i++)
{
if(l[ch[u][i]] == l[u]) //始终找最短的那个
{
u = ch[u][i];
printf("%c", i+'a');
break;
}
}
}
printf("\n");
}
int main(void)
{
int t, n, q, ca = 1;
cin >> t;
while(t--)
{
init();
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf(" %s", str), Insert(str);
scanf("%d", &q);
printf("Case %d:\n", ca++);
while(q--)
{
scanf(" %s", str);
Match(str);
}
}
return 0;
}