【題解】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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章