题目要求:
单词接龙,从指定索引的单词开始,每次接龙选择下一个单词的规则是:
下一个单词的首字母必须等于当前单词的尾字母。
如果有多个单词首字母相同,则选择长度最长的;如果长度也相同,则选择字典序最小的。
已经使用过的单词不能重复使用。
由于单词个数N最大为20,我们可以采用深度优先搜索(DFS)或者模拟的方式。
但注意,这里我们每次选择下一个单词时,只需要在所有未使用的单词中按照规则选择即可,因为规则是确定的,所以整个接龙过程是确定的。
思路:
读取起始索引K,单词个数N,以及单词列表。
将起始单词作为单词串的开始,并将其标记为已使用。
然后根据当前单词的尾字母,在未使用的单词中寻找所有首字母等于该尾字母的单词。
在这些单词中,按照规则(先比长度,长度相同比字典序)选择一个单词,将其加入单词串,并标记为已使用。
重复这个过程,直到没有可接的单词为止。
注意:规则中,当存在多个首字母相同的单词时,取长度最长的,如果长度也相等,则取字典序最小的。注意,字典序最小是指字符串比较中较小的那个。
由于每一步都是按照规则选取一个确定的单词,因此我们只需要循环直到无法选择为止。
但是,这里有一个注意点:可能同时有多个单词满足条件,但规则已经规定了如何选取,所以每次只选一个。
步骤:
读取输入。
初始化一个列表used,用于标记单词是否被使用。
从起始单词开始,将其加入结果字符串,并标记为已使用。
当前单词的尾字母为c,则在未使用的单词中查找首字母为c的单词,然后按照规则排序:先按长度降序,长度相同按字典序升序(因为要取字典序最小的,所以升序时第一个就是最小的)。
如果找到,则选择第一个单词(因为排序后第一个就是最长的,且字典序最小的),然后将其加入结果字符串,标记为已使用,并更新当前单词的尾字母。
重复步骤4-5,直到找不到符合条件的单词。
注意:排序规则:先按长度降序,如果长度相同,则按字典序升序。
例如:对于示例1,起始单词为"word",尾字母为'd',那么在以'd'开头的单词中,有"dd", "da", "dc", "dword", "d"(注意,起始单词已经使用,所以不能再用)。然后按照规则排序:
按长度:dword(长度5)> dd, da, dc, d(长度分别为2,2,2,1) -> 所以先选dword。
接着,当前单词变成dword,尾字母为'd',再从剩余的以'd'开头的单词中选:有"dd", "da", "dc", "d",同样按规则排序:dd, da, dc的长度都是2,d的长度为1,所以先考虑长度2的,然后按字典序:da, dc, dd(因为按字典序升序,da最小,所以先选da)。
但是,注意示例1的输出是"worddwordda",也就是说,接龙顺序是:word -> dword -> da。
因此,我们每次只选择一个单词,然后继续。
代码实现:
注意:起始单词已经使用,我们在循环开始前将起始单词标记为已使用,并放入结果字符串。
由于单词数量不多,我们可以直接模拟。
让我们按照步骤编写代码。
def input_contact():
k = int(input().strip())
n = int(input().strip())
char_list = [input().strip() for _ in range(n)]
return k, n, char_list
def init_game(k, n, char_list):
"""初始化游戏状态"""
start_char = char_list[k]
is_used = [False] * n
is_used[k] = True
result = [start_char]
current_char = start_char
return result, is_used, current_char
def find_candidates(is_used, current_char, char_list):
"""找到所有符合条件的候选单词"""
last_char = current_char[-1]
candidates = []
for i, word in enumerate(char_list):
if not is_used[i] and word[0] == last_char:
candidates.append((word, i)) # 同时保存单词和索引
return candidates
def select_best_candidate(candidates):
"""选择最佳候选单词"""
if not candidates:
return None, -1
# 按长度降序,字典序升序排序
candidates.sort(key=lambda x: (-len(x[0]), x[0]))
# 返回最佳候选单词及其索引
return candidates[0][0], candidates[0][1]
def find_long_str():
"""主函数:执行单词接龙"""
k, n, char_list = input_contact()
result, is_used, current_char = init_game(k, n, char_list)
while True:
# 找到所有候选单词
candidates = find_candidates(is_used, current_char, char_list)
# 选择最佳候选
best_word, best_index = select_best_candidate(candidates)
# 如果没有候选单词,结束游戏
if best_word is None:
break
# 更新状态
is_used[best_index] = True
current_char = best_word
result.append(best_word)
return ''.join(result)
if __name__ == '__main__':
result = find_long_str()
print(result)
总结
选择贪心策略的原因:
1. 题目规则是确定性的,每一步都有唯一最优解
2. 不需要考虑所有可能路径,因为选择规则固定
3. 贪心算法的时间复杂度低,适合N≤20的数据规模
4. 如果要求最长可能链,才需要用回溯或DFS我对题目的理解是:
1. 这是一个确定性的单词接龙,每一步的选择都是唯一的
2. 选择规则有明确优先级:
- 第一优先级:首字母匹配前一个单词的尾字母
- 第二优先级:在匹配的单词中选长度最长的
- 第三优先级:长度相同时选字典序最小的
3. 每个单词只能使用一次
4. 输出是单词直接拼接,没有空格时间复杂度:O(N²)
- 外层循环最多N次(每个单词用一次)
- 内层循环每次遍历N个单词找候选:O(N)
- 排序候选单词:O(MlogM),M≤N
空间复杂度:O(N)
- 存储使用标记和结果列表