生成保留可识别性的字符替换单词组合的内存优化问题
嘿,我来帮你捋捋这个问题,顺便给你一些实用的优化方案~
问题核心分析
你想要实现的是:给一组长度相同的单词,生成每个单词所有可能的「字母/下划线替换组合」,然后筛选出所有单词之间唯一的组合(比如原单词本身不同要保留,只在单个单词的组合列表里出现的下划线组合也要保留)。当前的核心痛点是长单词场景下,生成全量组合会导致内存爆炸——毕竟每个长度为n的单词会生成2ⁿ个组合,这个数会随n指数级增长,完全扛不住。
现有代码的关键问题
先拆解下你当前代码的几个明显问题:
- 内存爆炸根源:
get_combinations会提前生成某个单词的所有2ⁿ个组合并存在数组里,多个单词叠加后,内存占用直接飙升,长单词场景完全不可行。 - 去重逻辑低效:
compare_combinations用三重循环对比不同单词的组合,不仅时间复杂度极高,而且已经生成的大量重复组合还一直占着内存,纯纯的资源浪费。 - 数据结构冗余:把每个字符的选项存成二维数组,生成的组合又是带键的嵌套数组,后续还要多层循环拼接,既占空间又增加处理复杂度。
优化思路:实时生成+哈希表去重
我们可以换个思路不提前存储全量组合,而是用「哈希表(PHP里的关联数组)」来记录每个组合的出现次数,边生成边统计,最后直接筛选出出现次数为1的组合。这样做能把内存占用降到最低,同时大幅提升效率。
具体步骤:
- 先把所有原单词存入哈希表,标记它们的出现次数(如果原单词重复,次数会大于1,这类就不需要保留)。
- 对每个单词,逐个生成它的所有下划线组合,生成一个就往哈希表里更新一次计数,不需要存储所有组合。
- 最后遍历哈希表,收集所有出现次数为1的键,就是我们要的唯一组合集合。
优化后的代码实现
<?php function getUniqueCombinations($words) { $combinationCounts = []; // 第一步:先统计原单词的出现次数 foreach ($words as $word) { $combinationCounts[$word] = isset($combinationCounts[$word]) ? $combinationCounts[$word] + 1 : 1; } // 第二步:生成每个单词的所有下划线组合,实时更新计数 foreach ($words as $word) { $wordLength = strlen($word); // 用二进制掩码表示哪些位置替换为下划线:从1到2^length - 1(覆盖所有非全字母的组合) for ($mask = 1; $mask < (1 << $wordLength); $mask++) { $currentCombination = ''; for ($i = 0; $i < $wordLength; $i++) { // 检查掩码的第i位是否为1,是则替换为下划线,否则保留原字母 $currentCombination .= ($mask & (1 << $i)) ? '_' : $word[$i]; } // 更新该组合的计数 $combinationCounts[$currentCombination] = isset($combinationCounts[$currentCombination]) ? $combinationCounts[$currentCombination] + 1 : 1; } } // 第三步:筛选出仅出现一次的组合 $uniqueCombinations = []; foreach ($combinationCounts as $comb => $count) { if ($count === 1) { $uniqueCombinations[] = $comb; } } return $uniqueCombinations; } // 测试用例:单词"tea"和"tee" $inputWords = preg_split('/ +/', "tea tee"); $uniqueResults = getUniqueCombinations($inputWords); print_r($uniqueResults); ?>
代码细节解释
- 哈希表计数:
$combinationCounts作为核心容器,键是组合字符串,值是该组合的出现次数,重复组合只会存一次,极大节省内存。 - 二进制掩码生成组合:用二进制数
$mask来标记替换下划线的位置,比如长度为3的单词,mask=0b001表示第一个字符替换为下划线(得到_ea),mask=0b010表示第二个字符替换(得到t_a)。这种方式不需要递归或多层循环,用位运算就能高效生成所有组合。 - 结果筛选:最后只保留计数为1的组合,也就是只在单个单词的组合列表里出现过的内容,完全符合你的需求。
额外小贴士
- 如果要处理不同长度的单词,记得先按长度分组,因为不同长度的组合不可能重复,分组处理能进一步提升效率。
- 要是单词长度特别大(比如
n>20),2ⁿ还是会很大,这时候可以考虑在生成组合时,一旦发现某个组合的计数已经>=2,就跳过后续的重复统计(不过本质还是要靠哈希表去重)。 - 位运算可能需要你花点时间理解,比如
1 << $length等价于2^length,$mask & (1 << $i)用来判断掩码的第i位是否为1。
内容的提问来源于stack exchange,提问作者TryTryAgain
相关产品推荐
相关产品推荐

