字典树/Trie/前缀树-LeetCode总结:720词典中最长的单词;127. 单词接龙;677. 键值映射;面试题 17.17. 多次搜索;648. 单词替换

本文通过字典树结构解决LeetCode中的多个单词问题,包括寻找最长单词、最短转换序列、MapSum类的实现、字符串搜索以及继承词替换等。详细介绍了每个问题的解决方案,并给出了示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 MyTrie结构体和相关操作函数

typedef struct MyTrie {
    bool is_word;
    vector<MyTrie*> next;
    MyTrie():is_word(false) {
        next.resize(26,nullptr);
    }
}MT;
void insertWord(MT* cur, const string& word) {
    for (auto &w:word) {
        int c = w - 'a';
        if (!cur->next[c]) {
            auto tmp = new MT();
            cur->next[c] = tmp;
        }
        cur = cur->next[c];
    }
    cur->is_word = true;
}
bool hasWord(MT* cur, const string& word) {
    for (auto &w: word) {
        int c = w - 'a';
        if (!cur->next[c]) return false;
        cur = cur->next[c];
    }
    return cur->is_word;
}
vector<string> findChangeNeighbor(MT* cur, const string& word) {            // 127. 单词接龙
    vector<string> res;
    for (int i = 0; i < word.size(); ++i) {
        int c = word[i] - 'a';
        for (int j = 0; j < 26; ++j) {
            if (c == j) continue;
            if (cur->next[c] && hasWord(cur->next[c], word.substr(i + 1))) {
                auto tmp = word;
                tmp[i] = j + 'a';
                res.push_back(move(tmp));
            }
        }
        cur = cur->next[c];
    }
    return res;
}
bool isEveryStepWord(MT* cur, const string& word) {             
    for (auto &w: word) {
        int c = w - 'a';
        if (!cur->next[c] || !cur->next[c]->is_word) return false;
    }
    return true;
}
int getEverySetpWordMaxLength(MT* cur, const string& word) {
    int max_length = -1;
    queue<MT*>q;
    q.push(cur);
    while (q.size()) {
        auto qs = q.size();
        ++max_length;
        while (qs--) {
            auto tmp = q.front();
            q.pop();
            for (auto &i: tmp->next) {
                if (i && i->is_word) {
                    q.push(i);
                }
            }
        }
    }
    return max_length;
}
void findMaxLengthWordWithDFS(MT* cur, string& res_string, string path) {   // 720. 词典中最长的单词
    if (path.size() > res_string.size()) {
        res_string = path;
    }    
    for (int i = 0; i < 26; ++i) {
        auto w = cur->next[i];
        if (!w || !w->is_word) continue;
        findMaxLengthWordWithDFS(w, res_string, path + static_cast<char>(i + 'a'));
    }
}
vector<string> findWordStartWithPrefix(MT* cur, const string& prefix) {     // 677. 键值映射
    vector<string> res;
    for (int i = 0; i < prefix.size(); ++i) {
        int c = prefix[i] - 'a';
        if (!cur->next[c]) return {};
        cur = cur->next[c];
    }
    queue<pair<MT*, string>> q;
    q.push(make_pair(cur, prefix));
    while (q.size()) {
        auto qs = q.size();
        while (qs--) {
            auto [le, ri] = q.front();
            q.pop();
            if (le->is_word) 
                res.push_back(ri);
            for (int i = 0; i < 26; ++i) {
                if (!le->next[i]) continue;
                q.push(make_pair(le->next[i], ri + static_cast<char>(i + 'a')));
            }
        }
    }
    return res;
}
void findEveryWordPosition(MT* cur, const string& target,                   // 面试题 17.17. 多次搜索
                           vector<vector<int>>& res_pos,
                           unordered_map<string, int>& word_to_id) {
    for (int i = 0; i < target.size(); ++i) {           // i:pos_start
        int cur_i =target[i] - 'a';
        if (!cur->next[cur_i]) continue;
        auto cur_sub = cur->next[cur_i];
        for (int j = i + 1; j <= target.size(); ++j) {  // j:pos_end          
            if (cur_sub->is_word) {
                res_pos[word_to_id[target.substr(i, j - i)]].push_back(i);
            }
            if (j == target.size()) break;
            int cur_sub_i = target[j] - 'a';
            if (!cur_sub->next[cur_sub_i]) break;
            cur_sub = cur_sub->next[cur_sub_i];
        }
    }
}
string findMinPrefix(MT* cur, const string& word) {                          // 648. 单词替换
    string min_prefix;
    for (auto &w: word) {
        int c = w - 'a';
        if (!cur->next[c]) return word;
        min_prefix += w;
        if (cur->next[c]->is_word) return min_prefix;
        cur = cur->next[c];
    }
    return min_prefix;
}

给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。

若无答案,则返回空字符串。

 

示例 1:

输入:
words = ["w","wo","wor","worl", "world"]
输出:"world"
解释: 
单词"world"可由"w", "wo", "wor", 和 "worl"添加一个字母组成。


示例 2:

输入:
words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
输出:"apple"
解释:
"apply"和"apple"都能由词典中的单词组成。但是"apple"的字典序小于"apply"。


 

提示:


    所有输入的字符串都只包含小写字母。
    words数组长度范围为[1,1000]。
    words[i]的长度范围为[1,30]。

class Solution {
public:
    string longestWord(vector<string>& words) {
        auto mt = new MT();
        for (auto &w: words)
            insertWord(mt, w);
        string res_string;
        findMaxLengthWordWithDFS(mt, res_string, "");
        return res_string;
    }
};

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:


    每次转换只能改变一个字母。
    转换过程中的中间单词必须是字典中的单词。


说明:


    如果不存在这样的转换序列,返回 0。
    所有单词具有相同的长度。
    所有单词只由小写字母组成。
    字典中不存在重复的单词。
    你可以假设 beginWord 和 endWord 是非空的,且二者不相同。


示例 1:

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出: 5

解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的长度 5。


示例 2:

输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

输出: 0

解释: endWord "cog" 不在字典中,所以无法进行转换。

class Solution {
public:
	int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
		if (beginWord.size() == 0 || beginWord.size() != endWord.size()) return 0;
		int min_cnt = 1;
		MyTire *allWord = new MyTire(), *visited = new MyTire();
		insertWord(allWord, beginWord);
		for (int i = 0; i<wordList.size(); ++i)
			insertWord(allWord, wordList[i]);
		if (!hasWord(allWord, endWord)) return 0;
		queue<string> q;
		q.push(beginWord);
		while (q.size()) {
			int qSize = q.size();
			++min_cnt;
			while (qSize--) {
				auto cur = q.front();
				q.pop();
				vector<string> vec = findChangeNeighber(allWord, cur);
				for (int i = 0; i<vec.size(); ++i) {
					if (hasWord(visited, vec[i])) continue;
					if (vec[i] == endWord) return min_cnt;
					q.push(vec[i]);
					insertWord(visited, vec[i]);
				}
			}
		}
		return 0;
	}
};

实现一个 MapSum 类里的两个方法,insert 和 sum。

对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。

对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。

示例 1:

输入: insert("apple", 3), 输出: Null
输入: sum("ap"), 输出: 3
输入: insert("app", 2), 输出: Null
输入: sum("ap"), 输出: 5

 

class MapSum {
    unordered_map<string, int> m;
    MT *mt;
public:
    /** Initialize your data structure here. */
    MapSum() {
        mt = new MT();
    }    
    void insert(string key, int val) {
        m[key] = val;
        insertWord(mt, key);
    }    
    int sum(string prefix) {
        auto vec = findWordStartWithPrefix(mt, prefix);
        int res_sum = 0;
        for (auto &v: vec) {
            res_sum += m[v];
        }
        return res_sum;
    }
};

给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]为smalls[i]出现的所有位置。

示例:

输入:
big = "mississippi"
smalls = ["is","ppi","hi","sis","i","ssippi"]
输出: [[1,4],[8],[],[3],[1,4,7,10],[5]]


提示:


    0 <= len(big) <= 1000
    0 <= len(smalls[i]) <= 1000
    smalls的总字符数不会超过 100000。
    你可以认为smalls中没有重复字符串。
    所有出现的字符均为英文小写字母。

class Solution {
public:
    vector<vector<int>> multiSearch(string big, vector<string>& smalls) {
        vector<vector<int>> res_vec(smalls.size());
        if (big.empty() || smalls.empty()) return res_vec;        
        unordered_map<string, int> word_to_id;
        auto mt = new MT();
        for (int i = 0; i < smalls.size(); ++i) {
            word_to_id[smalls[i]] = i;
            insertWord(mt, smalls[i]);
        }
        findEveryWordPosition(mt, big, res_vec, word_to_id);
        return res_vec;
    }
};

在英语中,我们有一个叫做 词根(root)的概念,它可以跟着其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如,词根an,跟随着单词 other(其他),可以形成新的单词 another(另一个)。

现在,给定一个由许多词根组成的词典和一个句子。你需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。

你需要输出替换之后的句子。

 

示例:

输入:dict(词典) = ["cat", "bat", "rat"] sentence(句子) = "the cattle was rattled by the battery"
输出:"the cat was rat by the bat"


 

提示:


    输入只包含小写字母。
    1 <= dict.length <= 1000
    1 <= dict[i].length <= 100
    1 <= 句中词语数 <= 1000
    1 <= 句中词语长度 <= 1000

class Solution {
public:
    string replaceWords(vector<string>& dictionary, string sentence) {
        auto mt = new MT();
        for (auto &d: dictionary) {
            insertWord(mt, d);
        }
        stringstream ss(sentence);
        string res_string, s;
        while (getline(ss, s, ' ')) {
            res_string += findMinPrefix(mt, s) + ' ';
        }
        res_string.pop_back();
        return res_string;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值