1. 题号和题目名称
- 连接词
2. 题目叙述
给定一个不含重复单词的列表,编写一个程序,返回给定单词列表中所有的连接词。
连接词的定义为:一个字符串完全是由至少两个给定数组中的单词组成的。
示例 1:
输入:["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]
输出:["catsdogcats","dogcatsdog","ratcatdogcat"]
解释:"catsdogcats" 由 "cats", "dog" 和 "cats" 组成;
"dogcatsdog" 由 "dog", "cats" 和 "dog" 组成;
"ratcatdogcat" 由 "rat", "cat", "dog" 和 "cat" 组成。
示例 2:
输入:["cat","dog","catdog"]
输出:["catdog"]
3. 模式识别
这是一个字符串匹配和组合的问题。可以通过构建字典树(Trie)来优化字符串的查找过程,然后利用深度优先搜索(DFS)来判断一个单词是否可以由其他单词组成。
4. 考点分析
- 字典树(Trie)的应用:用于高效存储和查找单词。
- 深度优先搜索(DFS):用于遍历字典树,判断单词是否为连接词。
5. 所有解法
- 字典树 + DFS 解法:先构建字典树,然后对每个单词进行 DFS 检查,这是最优解法。
6. 最优解法(字典树 + DFS 解法)的 C 语言代码
// 定义字典树(Trie)节点结构体
// Trie 是一种树形数据结构,常用于高效地存储和检索字符串集合
typedef struct Trie {
// 每个节点有 26 个指针,分别对应 26 个小写英文字母
// children[i] 表示当前节点连接到字符 'a' + i 的子节点
struct Trie * children[26];
// 标记当前节点是否为一个单词的结尾
// 如果 isEnd 为 true,则表示从根节点到当前节点的路径所构成的字符串是一个完整的单词
bool isEnd;
}Trie;
// 宏定义,用于初始化 Trie 节点
// node: 待初始化的 Trie 节点指针
/* 遍历 26 个字母对应的子节点指针,将其初始化为 NULL*/
// 将当前节点标记为非单词结尾
#define TRIE_INITIAL(node) do {
\
for(int i = 0; i < 26; ++i) {
\
(node)->children[i] = NULL; \
} \
(node)->isEnd = false; \
}while(0);
// 递归释放 Trie 树的内存
// node: 待释放的 Trie 树的根节点指针
static void freeTrie(Trie * node) {
// 如果节点为空,直接返回
if (NULL == node) {
return;
}
// 遍历 26 个字母对应的子节点
for (int i = 0; i < 26; ++i) {
// 如果子节点不为空,则递归释放该子节点及其子树
if (node->children[i] != NULL) {
freeTrie(node->children[i]);
}
}
// 释放当前节点的内存
free(node);
}
// 比较函数,用于 qsort 排序
// pa: 指向第一个字符串指针的指针
// pb: 指向第二个字符串指针的指针
// 返回值:如果第一个字符串长度小于第二个字符串长度,返回负数;相等返回 0;大于返回正数
static int cmp(const void * pa, const void * pb){
// 获取第一个字符串的长度
int la = strlen(*(char **)pa);
// 获取第二个字符串的长度
int lb = strlen(*(char **)pb);
// 返回长度差值
return la - lb;


1万+

被折叠的 条评论
为什么被折叠?



