【题解】QDUOJ.65.again and again

描述

xx学习了Trie树后,向你问了一个问题,给定一个字符串集合S={str1, str2,
…,strn}和一个字符串s,在s的后面接尽量少的字符,使其属于集合S。当然如果s本身就属于S,s就是答案。

输入

第一行一个正整数T(T <= 10),表示有T组数据。 每组数据输入格式如下:
第一行为一个正整数N(0 < N < 20000),表示字符串集合内的字符串数。 接下来N行,每行一个字符串,表示集合中的串。
接下来一行是一个正整数Q(0 < Q < 200),表示有Q次询问。 接下来Q行,每行一个字符串str。
所有字符串均由小写英文字母组成,且1 <= 长度 <= 20。

输出

每组数据输出格式如下: 先输出“Case x:”,x表示是第几组数据,然后输出一个换行。
接下来输出Q行,对于每次询问,如果str能够添加字符使其属于字符串集合,则输出长度最小的(即str添加字符后构成的串),有多个满足条件的话输出字典序最小的。否则输出-1。

思路与解法

构造字典树的时候动态更新记录以下信息:

1. 每个子节点后连接的最短单词的长度
2. 当前节点是否是某单词的结尾
3. 当前节点后所连接的最短或字典序最小的单词的子节点是哪个

查询时,若存在满足条件的单词,则根据查询后停留的节点信息,循环补全单词即可。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define M 400001
#define INF 100
struct TT{
    int l[27];          // l[0]~l[25]记录以各子节点为起点存在的最短单词长度, l[26]记录l[0]~l[25]中最短长度的下标值。初始为INF
    struct TT *p[27];   // p[0]~p[25]记录个子节点地址,p[26]记录是否存在以该节点结尾的单词,若没有则为0
} *tt, mem[M];  // mem[M]预分配内存
int inx;

struct TT * qmalloc(){  // 自定义节点内存分配函数,初始化节点。
    struct TT *p = mem + inx++;
    for (int i = 0; i < 27; p->p[i++] = 0)
        p->l[i] = INF;
    p->l[26] = 0;
    return p;
}
void add(const char *s){    // 插入字典树
    struct TT *p;
    int l = strlen(s);  
    for (p = tt; *s; l--){  // l 维持插入单词长度
        int i = *s++ - 'a';
        if(!p->p[i]) p->p[i] = qmalloc();
        if (p->l[i] > l)     p->l[i] = l; // 当插入单词长度小于当前节点对应子节点最小长度时更新
        if (p->l[p->l[26]] > l || p->l[p->l[26]] == l && p->l[26] > i)  // 当插入单词长度小于当前节点最小长度或字典序更小时更新
            p->l[26] = i;
        p = p->p[i];
    }
    p->p[26] = p;   // 标记单词结束的节点
}
void ff(const char * s){
    struct TT *p;
    const char *tmps = s;
    for (p = tt; *s; p = p->p[*s++ - 'a'])
        if (!p->p[*s - 'a']){   // 没有查到时直接返回
            printf("-1\n");
            return;
        }

    // 首先输出原单词,根据l[26]中记录的最短子节点循环补全字母,直到当前节点存在结束标记
    for (printf(tmps); !p->p[26]; p = p->p[p->l[26]])
        printf("%c", p->l[26] + 'a');
    puts("");
};
int main(void)
{
    int n, T, t;
    char ss[30];
    scanf("%d", &T);
    for (t = 1; T--; t++){
        inx = 0;        // 初始化预分配内存
        tt = qmalloc(); // 初始化字典树
        for (scanf("%d", &n); n--; add(ss))
            scanf("%s", ss);
        printf("Case %d:\n", t);
        for (scanf("%d", &n); n--; ff(ss))
            scanf("%s", ss);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值