字母乱序字符串转数字求和的Top-Down DP代码错误排查
问题排查与修复思路
核心问题分析
你的Top-Down DP方案在短字符串正常、长字符串输出0,本质是状态设计、匹配逻辑或递归返回逻辑存在漏洞,导致长字符串的递归路径无法找到有效解,最终返回默认的0值(而非正确累加)。
常见错误点及修复方案
1. 状态设计缺陷:$标记导致状态混淆
用$标记已使用字符再结合排序字符串作为DP状态,容易出现相同剩余字符但$位置不同的重复状态,或者替换$时的字符位置错误,导致缓存的状态结果无效。
修复:
放弃$标记,直接用剩余字符的排序字符串作为DP状态。匹配数字拼写后,直接从原字符串中移除对应字符,将剩余字符重新排序生成新状态。这样状态唯一且准确,避免$带来的歧义。
2. 数字匹配逻辑:逐个替换而非计数校验
如果代码是逐个查找并替换字符(比如找第一个'o'、第一个'n'来匹配"one"),长字符串中可能出现替换错误的字符,导致剩余字符无法组成后续数字,最终递归返回0。
修复:
用字符计数的方式校验匹配:
- 对当前字符串和目标数字的拼写,分别统计每个字符的出现次数
- 若数字拼写的所有字符计数都≤当前字符串的计数,说明可以匹配
- 生成剩余字符串时,将当前字符串的每个字符计数减去数字拼写的对应计数,再将剩余字符拼接排序成新状态
3. 递归返回逻辑:错误返回0而非回溯
如果代码中遇到某个数字匹配失败时直接返回0,而不是跳过该数字尝试其他可能,长字符串中只要有一个分支失败就会直接返回0,忽略了其他有效路径。
修复:
- 递归终止条件:当剩余字符串为空时,返回0(无剩余字符可组成数字)
- 遍历所有数字拼写,对每个可匹配的数字,递归计算剩余状态的结果,加上当前数字的值,作为当前状态的候选结果
- 因为题目明确输入是合法的(所有字符都能组成数字),所以必然存在至少一个有效候选结果,取该结果作为当前状态的缓存值返回
4. 记忆化缓存:错误缓存无效状态
如果缓存中错误地将某个中间状态的结果存为0(比如某次递归失败时的返回值),后续遇到该状态会直接取0,导致整个递归链错误。
修复:
- 缓存只存储有效结果:只有当递归得到的结果是合法的(比如≥0,因为数字总和非负),才存入缓存
- 初始时缓存中不存储0值,只有当剩余字符串为空时才返回0
代码修正示例片段
# 数字与对应拼写的映射 num_map = [ (0, "zero"), (1, "one"), (2, "two"), (3, "three"), (4, "four"), (5, "five"), (6, "six"), (7, "seven"), (8, "eight"), (9, "nine") ] cache = {} def dp(s): if not s: return 0 if s in cache: return cache[s] total = 0 for num, word in num_map: # 统计当前字符串和单词的字符计数 s_count = {} for c in s: s_count[c] = s_count.get(c, 0) + 1 word_count = {} for c in word: word_count[c] = word_count.get(c, 0) + 1 # 检查是否可以匹配 valid = True for c in word_count: if s_count.get(c, 0) < word_count[c]: valid = False break if valid: # 生成剩余字符串 remaining = [] for c in s_count: remaining.extend([c] * (s_count[c] - word_count.get(c, 0))) remaining_s = ''.join(sorted(remaining)) # 递归计算 res = num + dp(remaining_s) # 题目输入为唯一解,找到即可返回 total = res break cache[s] = total return total # 测试长字符串 input_str = "towsnveefourfiveone" sorted_input = ''.join(sorted(input_str)) print(dp(sorted_input)) # 输出19
关键验证点
- 运行修正后的代码测试长字符串,确认返回19
- 检查缓存中的状态是否正确对应剩余字符的总和
- 确保每个数字匹配时的字符计数逻辑正确,不会出现漏减或多减的情况
内容的提问来源于stack exchange,提问作者htaritoyj
相关产品推荐
相关产品推荐

