求助:高效生成20个letter pair组成10个4-letter word的合法组合
问题描述
给定一个包含20个字母对的数组:
['BA', 'BO', 'CO', 'CR', 'DA','FA', 'FE', 'FI', 'GL', 'LL','MA', 'NE', 'OW', 'OW', 'RE','RK', 'RN', 'SK', 'ST', 'TS']
数组中的字母对可重复出现。
同时给定一个4字母单词数组:
['BALL','BANE','BARE','BARK','BARN','BASK','BAST','BATS','BOLL','BONE','BORE','BORK','BORN','BOSK','BOTS','CODA','COMA','CONE','CORE','CORK','CORN','COST','COTS','CROW','DARE','DARK','DARN','FALL','FANE','FARE','FAST','FATS','FELL','FERE','FERN','FEST','FETS','FICO','FIFE','FILL','FINE','FIRE','FIRN','FIST','FITS','GLOW','MALL','MANE','MARE','MARK','MASK','MAST','MATS','NEMA','NEST','NETS','REST','RETS','STOW']
所有单词均来自词典,且每个单词可拆分为两个合法字母对。
需要生成所有合法组合:
- 每个组合包含10个唯一的4字母单词(均来自上述数组)
- 每个单词由两个字母对拼接而成(如
CO+RK→CORK,BA+TS→BATS) - 组合需将20个字母对恰好使用一次,且字母对使用次数不超过其在原数组中的出现次数
当前暴力枚举方案耗时过长(80-100小时)或内存占用过高,需要高效的实现方法。
高效实现方案
1. 预构建字母对映射表
先把所有4字母单词拆解为字母对组合,构建字典快速查询:
- 键:单个字母对
- 值:所有以该字母对作为前半部分的单词,以及对应的后半字母对
示例结构:
pair_map = { 'BA': [('BALL', 'LL'), ('BANE', 'NE'), ('BARE', 'RE'), ...], 'CO': [('CODA', 'DA'), ('COMA', 'MA'), ('CONE', 'NE'), ...], # 覆盖所有字母对的映射 }
同时用计数器(如collections.Counter)统计原字母对数组的使用次数,方便后续快速判断剩余可用次数。
2. 回溯剪枝算法
采用深度优先搜索(DFS)+剪枝的方式遍历可能的组合,核心逻辑:
- 每次选择未使用过的单词,检查其两个字母对是否还有剩余可用次数
- 选择后,扣除对应字母对的使用次数,将单词加入当前组合
- 当组合包含10个单词时,记录为合法组合
- 回溯时,恢复字母对的使用次数,移除当前单词
关键剪枝策略:
- 优先用稀有字母对:先处理原数组中出现次数少的字母对(比如
GL仅出现1次),快速排除无效路径,减少分支数 - 跳过重复单词:用集合记录已选单词,避免重复选择
- 提前终止无效路径:若剩余字母对数量为奇数,或剩余字母对无法配对成单词,直接终止该分支
3. 内存与性能优化
- 用迭代式DFS代替递归,避免栈溢出,更易控制内存
- 用位标记或哈希集合快速判断单词是否已使用
- 预筛选单词:提前排除包含原数组中没有的字母对的单词(题目已保证单词合法,此步骤可省略)
示例代码框架(Python)
from collections import Counter # 输入数据 letter_pairs = ['BA', 'BO', 'CO', 'CR', 'DA','FA', 'FE', 'FI', 'GL', 'LL','MA', 'NE', 'OW', 'OW', 'RE','RK', 'RN', 'SK', 'ST', 'TS'] word_list = ['BALL','BANE','BARE','BARK','BARN','BASK','BAST','BATS','BOLL','BONE','BORE','BORK','BORN','BOSK','BOTS','CODA','COMA','CONE','CORE','CORK','CORN','COST','COTS','CROW','DARE','DARK','DARN','FALL','FANE','FARE','FAST','FATS','FELL','FERE','FERN','FEST','FETS','FICO','FIFE','FILL','FINE','FIRE','FIRN','FIST','FITS','GLOW','MALL','MANE','MARE','MARK','MASK','MAST','MATS','NEMA','NEST','NETS','REST','RETS','STOW'] # 构建字母对到单词+后半对的映射 pair_map = {} for word in word_list: first_pair = word[:2] second_pair = word[2:] if first_pair not in pair_map: pair_map[first_pair] = [] pair_map[first_pair].append( (word, second_pair) ) # 统计字母对初始计数 pair_counter = Counter(letter_pairs) total_words_needed = 10 valid_combinations = [] def dfs(current_combination, current_counter): if len(current_combination) == total_words_needed: valid_combinations.append(current_combination.copy()) return # 剪枝:剩余字母对数量必须是偶数,否则无法组成完整单词 remaining_pairs = sum(current_counter.values()) if remaining_pairs % 2 != 0: return # 优先遍历稀有字母对对应的单词,减少分支 available_pairs = [p for p in current_counter if current_counter[p] > 0] available_pairs.sort(key=lambda x: current_counter[x]) used_words = set(current_combination) for pair in available_pairs: if pair not in pair_map: continue # 没有以该字母对开头的单词,跳过 for word, second_pair in pair_map[pair]: if word in used_words: continue # 检查第二个字母对是否可用 if current_counter.get(second_pair, 0) == 0: continue # 选择该单词,更新计数 current_counter[pair] -= 1 if current_counter[pair] == 0: del current_counter[pair] current_counter[second_pair] -= 1 if current_counter[second_pair] == 0: del current_counter[second_pair] current_combination.append(word) # 递归搜索 dfs(current_combination, current_counter) # 回溯,恢复计数 current_combination.pop() current_counter[pair] = current_counter.get(pair, 0) + 1 current_counter[second_pair] = current_counter.get(second_pair, 0) + 1 # 启动DFS dfs([], pair_counter.copy()) # 输出结果 print(f"找到{len(valid_combinations)}个合法组合") for combo in valid_combinations: print(combo)
额外优化建议
- 预计算单词的字母对使用情况,避免每次拆解单词
- 对单词按前/后字母对分组,减少遍历次数
- 使用多线程/多进程并行搜索不同初始分支(注意线程安全,每个线程使用独立的计数器和组合)
内容的提问来源于stack exchange,提问作者thefuzzy0ne
相关产品推荐
相关产品推荐

