字典树再战

昨天校赛,遇到了一道字典树的题目,卡了挺久,原因不是不会写,而是被vector指针初始化坑了(现在改用数组了,哼)。感觉得好好总结一下字典树,下面先po这个问题,然后写个字典树模板,最后将模板修改成能够解决这个问题。

A. 问题描述

给定n个字符串(长度不超过8)构成一个字典,m个字符串作为查询,问查询的字符串是否有可能在字典中。为什么是可能呢?因为字典里有某些单词因为bug,可能有的字符被变成了’?’这个符号,相当于这个符号变成了通配符,比如c?t可以匹配cat,cbt,cct,…,cut等等。输入保证都是小写英文字母,字典里的单词最多只有一个字符是英文的问号。

B. 字典树通用模板?

可能还不够通用,不过也算是模板了:

#include 
#include 
#include 
#include 
using namespace std;

const int CharsetSize = 26;

class Node
{
private:
    bool final;     // final为true表示有以该节点结尾的单词出现过
    Node** children;

public:
    Node() : final(false), children(NULL) {}

    void ensureChildrenNotEmpty() {
        if (children == NULL) {
            children = new Node*[CharsetSize];
            for (int i = 0; i < CharsetSize; ++i)
                children[i] = new Node();
        }
    }

    void insert(const string& s) {
        insertHelper(this, s, 0);
    }

    bool find(const string& s) {
        return findHelper(this, s, 0);
    }

private:
    void insertHelper(Node* root, const string& s, int index) {
        if (index == s.size()) {
            root->final = true;
            return ;
        }

        int key = s[index] - 'a';
        root->ensureChildrenNotEmpty();
        root->insertHelper(root->children[key], s, index + 1);
    }

    bool findHelper(Node* root, const string& s, int index) {
        if (index == s.size())
            return root->final;

        if (root->children == NULL)
            return false;

        int key = s[index] - 'a';
        return findHelper(root->children[key], s, index + 1);
    }
};

int main() {
    int n, m;
    cin >> n >> m;
    Node* root = new Node();
    string buf;

    for (int i = 0; i < n; ++i) {
        cin >> buf;
        root->insert(buf);
    }

    for (int i = 0; i < m; ++i) {
        cin >> buf;
        if (root->find(buf))
            printf("[%s] Found\n", buf.data());
        else
            printf("[%s] Not Found\n", buf.data());
    }

    return 0;
}

C. 问题分析

这个问题关键在于’?’如何表示和匹配,直接用26个分支代替它吗?很明显是不明智的做法,复杂度没必要搞到这么大,万一字符集的大小是128呢?岂不是内存浪费太多了?
所以可以考虑给它加多一个分支,特殊判断就行了。比如(注意加注释的地方):

#include 
#include 
#include 
#include 
using namespace std;

const int CharsetSize = 27;

class Node
{
private:
    bool final;     // final为true表示有以该节点结尾的单词出现过
    bool taken;     // 需要加多一个标记来记录某个分支是否被占用
    Node** children;

public:
    Node() : final(false), taken(false), children(NULL) {}

    void ensureChildrenNotEmpty() {
        if (children == NULL) {
            children = new Node*[CharsetSize];
            for (int i = 0; i < CharsetSize; ++i)
                children[i] = new Node();
        }
    }

    void insert(const string& s) {
        insertHelper(this, s, 0);
    }

    bool find(const string& s) {
        return findHelper(this, s, 0);
    }

private:
    void insertHelper(Node* root, const string& s, int index) {
        if (index == s.size()) {
            root->final = true;
            return ;
        }

        int key = s[index] - 'a';
        // 特殊的问号放在最后面的分支
        if (s[index] == '?')
            key = CharsetSize - 1;
        root->ensureChildrenNotEmpty();
        root->children[key]->taken = true;
        root->insertHelper(root->children[key], s, index + 1);
    }

    bool findHelper(Node* root, const string& s, int index) {
        if (index == s.size())
            return root->final;

        // 无法往下搜索,即无法匹配
        if (root->children == NULL)
            return false;

        int key = s[index] - 'a', tail = CharsetSize - 1;

        // 如果有正常的分支,还要考虑是否有?匹配的分支。若有,则结果是两者的or
        if (root->children[key]->taken) {
            bool result = findHelper(root->children[key], s, index + 1);
            if (root->children[tail]->taken)
                return result || findHelper(root->children[tail], s, index + 1);
            return result;
        }

        // 否则,只剩下?的分支可以匹配了
        return root->children[tail]->taken && findHelper(root->children[tail], s, index + 1);
    }
};

int main() {
    int n, m;
    cin >> n >> m;
    Node* root = new Node();
    string buf;

    for (int i = 0; i < n; ++i) {
        cin >> buf;
        root->insert(buf);
    }

    for (int i = 0; i < m; ++i) {
        cin >> buf;
        if (root->find(buf))
            printf("[%s] Found\n", buf.data());
        else
            printf("[%s] Not Found\n", buf.data());
    }

    return 0;
}

D. 测试样例

Case 1:
3 4
cu?e
?irl
hello
ctue
girl
irl
hello

Case 2:
1 1
?
z

你可能感兴趣的:(题解,算法,笔试面试)