如何用递归实现可重复使用单词的字符串构造计数

3次阅读

如何用递归实现可重复使用单词的字符串构造计数

本文详解如何修正递归函数,使其支持从词库中重复选取同一单词来拼凑目标字符串,并给出正确、高效、可读性强的实现方案。

本文详解如何修正递归函数,使其支持从词库中**重复选取同一单词**来拼凑目标字符串,并给出正确、高效、可读性强的实现方案。

在原始代码中,count_construct 函数采用索引 index 控制词库遍历顺序,每次递归调用要么“包含当前词”(helper(index, …)),要么“跳过当前词”(helper(index + 1, …))。这种设计本质上模拟了子集枚举——每个单词最多被使用一次,因此无法生成如 “p” + “ur” + “p” + “le” 这类需重复使用 “p” 的合法组合,导致结果偏小(返回 1 而非正确的 2 或更多)。

要支持无限次复用任意单词,关键在于:递归分支不应受限于词库索引的单向推进,而应允许每次从整个 word_bank 中自由选择。同时,为避免无效穷举,我们采用前缀匹配剪枝:仅当 word 能匹配 target 当前剩余前缀时,才尝试选用该词。

以下是优化后的标准实现(无全局变量、无副作用、纯递归、清晰易懂):

def count_construct(target, word_bank):     # 基础情况:已完全匹配     if target == "":         return 1      total = 0     # 尝试每个单词作为当前前缀     for word in word_bank:         # 检查是否能匹配 target 开头         if len(word) <= len(target) and target.startswith(word):             remainder = target[len(word):]  # 剩余待匹配部分             total += count_construct(remainder, word_bank)  # 递归求解剩余部分      return total  # 测试用例 target_example = "purple" word_bank_example = ["purp", "p", "ur", "le", "purpl"] print(count_construct(target_example, word_bank_example))  # 输出: 2

为什么这个版本正确?

  • 每次递归都对整个 word_bank 进行遍历,”p” 可在不同递归层级被多次选用;
  • target.startswith(word) 确保只探索合法路径,避免无效拼接(如 “le” 放在 “purple” 开头);
  • remainder = target[len(word):] 精确推进匹配位置,天然支持重叠与复用;
  • 无状态变量、无 nonlocal/global,函数纯净、可测试、易推理。

⚠️ 注意事项:

  • 算法时间复杂度为指数级(最坏 O(n^m),其中 m 是 target 长度,n 是词库大小),对长目标或大词库可能较慢;
  • 实际项目中建议添加记忆化(memoization) 缓存已计算的 target 子问题结果,将时间复杂度优化至 O(m × n × k)(k 为平均单词长度);
  • 若需枚举所有方案而非仅计数,可改用生成器或传入 path 列表收集路径(如答案中 print(f’Found solution: {current}’) 所示)。

总结:递归设计的核心在于正确定义子问题。本例中,“构造 target” 的子问题是“构造 target[len(word):]”,而非“从第 i 个词开始构造”。抛弃索引依赖、拥抱语义化子问题划分,是解决此类可重复组合问题的关键。

text=ZqhQzanResources