技术问询:求长度为7的字符串合法填充方式数(a≤x次、b≤y次)
字符串填充计数问题:修正DFS回溯+数学优化方案
一、修正你的DFS回溯法
你用DFS回溯漏情况,大概率是两个核心问题:
- 没考虑到除a、b外的24种字符是独立选择(每个字符都算不同的填充方式,不是只算1种)
- 递归分支的边界条件或状态跟踪不全,比如没处理完7个字符就提前终止,或对a/b的使用次数判断错误
下面是修正后的回溯实现,用返回值统计组合数,逻辑更清晰:
def count_valid_fillings(x, y): # 递归函数:返回从当前位置pos开始,已用a_used个a、b_used个b时的合法填充数 def backtrack(pos, a_used, b_used): # 填充完7个字符,算1种合法方式 if pos == 7: return 1 res = 0 # 选择a:如果还没超过x次限制 if a_used < x: res += backtrack(pos + 1, a_used + 1, b_used) # 选择b:如果还没超过y次限制 if b_used < y: res += backtrack(pos + 1, a_used, b_used + 1) # 选择其他24种字符:每个字符都是独立的,所以乘24再加上后续的组合数 res += 24 * backtrack(pos + 1, a_used, b_used) return res # 从第0个位置开始,a、b都没用过 return backtrack(0, 0, 0)
二、更高效的数学解法(容斥原理)
对于固定长度的场景,用数学公式计算比回溯更快,核心是容斥原理:
- 先算所有可能的填充数:
26^7(每个位置26种选择) - 减去a出现次数超过x的情况
- 减去b出现次数超过y的情况
- 加回同时a超x且b超y的情况(因为前面减了两次,需要补回来)
具体实现代码(用Python的组合数计算):
import math def count_valid_fillings_math(x, y): total = 26 ** 7 # 计算a出现次数超过x的非法填充数 invalid_a = 0 for a_cnt in range(x + 1, 8): # 选a_cnt个位置放a,剩下的位置可以是除a外的25种字符(包括b) invalid_a += math.comb(7, a_cnt) * (25) ** (7 - a_cnt) # 计算b出现次数超过y的非法填充数 invalid_b = 0 for b_cnt in range(y + 1, 8): invalid_b += math.comb(7, b_cnt) * (25) ** (7 - b_cnt) # 计算同时a超x且b超y的非法填充数(容斥补回) invalid_ab = 0 for a_cnt in range(x + 1, 8): remaining_pos = 7 - a_cnt # 剩下的位置不够放至少y+1个b,直接跳过 if remaining_pos < y + 1: continue for b_cnt in range(y + 1, remaining_pos + 1): # 选a_cnt个位置放a,再从剩下的位置选b_cnt个放b,最后剩下的放其他24种字符 invalid_ab += math.comb(7, a_cnt) * math.comb(remaining_pos, b_cnt) * (24) ** (7 - a_cnt - b_cnt) # 合法总数 = 总数 - 非法a - 非法b + 重复减去的非法ab return total - invalid_a - invalid_b + invalid_ab
验证示例
比如x=2,y=3:两种方法计算结果完全一致,不会遗漏任何合法填充情况。
内容的提问来源于stack exchange,提问作者Abhishek Singh
相关产品推荐
相关产品推荐

