程序员面试金典 17.25

本文深入探讨了WordRectangle算法,一种利用字典中的单词构造最大矩阵的问题。通过详细解释算法思路,包括字典分组、Trie树构建及深度优先搜索的应用,提供了清晰的代码实现与优化策略。

Word Rectangle:给定一个字典,使用字典中的单词组成一个最大的矩阵,该矩阵中每行和每列都是字典中的单词,单词可以重复使用。

最喜欢这种题了,反正也没什么取巧的方法,直接搜索就好了,主要看代码能力。因为要返回最大的矩阵,所以我们从最大的边长开始,慢慢缩小搜索边长。返回类型为vector<string>,根据其特点,每次添加矩阵的一行较为方便,然后在新得到的部分矩阵中,去检查每一列是否是字典中单词的前缀即可。

题解中的很多方法都是使用整个字典构建的Trie树,不过我感觉这样子不利于剪枝,所以我借鉴了书上的方法,将字典中的单词按照长度进行了分组,用Group表示,每个Group中包含两个成员,一个unordered_set<string> WordSet用来记录单词,另一个Trie root用来进行前缀查找。

每次搜索都需要指定这次要搜索的矩阵的lengthheight。当在矩阵中添加新的一行后,直接查找每一列是否在Group[height]root中即可。

class Solution {
private:
    struct TrieNode
    {
        char ch;
        array<shared_ptr<TrieNode>, 26> Children;
        TrieNode(char c = '\0') : ch(c){}
    };
    struct Trie
    {
        shared_ptr<TrieNode> RootPointer;
        Trie(){ RootPointer = make_shared<TrieNode>(); }
        void addString(const string &str)
        {
            shared_ptr<TrieNode> NodePointer = RootPointer;
            for(char c : str)
            {
                if(NodePointer->Children[c - 'a'] == nullptr){
                    NodePointer->Children[c - 'a'] = make_shared<TrieNode>(TrieNode(c));
                }
                NodePointer = NodePointer->Children[c - 'a'];
            }
        }
        bool contain(const vector<string> &rect, const size_t col)
        {
            shared_ptr<TrieNode> NodePointer = RootPointer;
            for(const string &word : rect)
            {
                char c = word[col];
                if(NodePointer->Children[c - 'a'] == nullptr) return false;
                else NodePointer = NodePointer->Children[c - 'a'];
            }
            return true;
        }
    };
    struct Group
    {
        unordered_set<string> WordSet;
        Trie root;
        bool empty(){ return WordSet.empty(); }
        void addWord(const string &word)
        {
            WordSet.insert(word);
            root.addString(word);
        }
        bool partialContain(const vector<string> &rect, const size_t col){ return root.contain(rect, col); }
    };
    vector<Group> Groups;
    vector<string> ans, rect;
    bool Found = false;
    void divideWordByLength(const vector<string> &words)
    {
        for(const string &word : words)
        {
            if(word.length() >= Groups.size()) Groups.resize(word.length() + 1);
            Groups[word.length()].addWord(word);
        }
    }
    void DFS(size_t length, size_t height)
    {
        if(rect.size() == height){
            Found = true;
            ans = rect;
            return;
        }
        const Group &group = Groups[length];
        for(const string &word : group.WordSet)
        {
            if(!Found){
                rect.push_back(word);
                if(check(rect, height)){
                    DFS(length, height);
                }
                rect.pop_back();
            }
        }
    }
    bool check(const vector<string> &rect, const size_t height)
    {
        Group &group = Groups[height];
        for(size_t col = 0; col < rect[0].size(); col++)
        {
            if(!group.partialContain(rect, col)) return false;
        }
        return true;
    }
public:
    vector<string> maxRectangle(vector<string>& words) {
        divideWordByLength(words);
        for(size_t length = Groups.size(); length > 1; length--)
        {
            for(size_t height = length; height > 1; height--)
            {
                if(!Groups[length - 1].empty() && !Groups[height - 1].empty()){
                    DFS(length - 1, height - 1);
                    if(!ans.empty()) return ans;
                }
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值