基于数位DP统计[1,n]中字典序小于指定数的正整数问题排查
问题排查与修正:字典序小于指定数的正整数统计
你的代码输出错误的核心原因是把数值大小比较和字典序比较搞混了,数位DP的状态设计没有针对字典序的逐位匹配逻辑,反而可能按数值范围统计,导致结果严重偏差。
错误分析
测试用例count(7,13)中,正确的字典序小于"7"的数是:1、10、11、12、13、2、3、4、5、6,共10个。原代码输出35,明显是错误统计了数值小于7的数之外的无效项,逻辑完全偏离字典序规则。
正确的数位DP实现思路
字典序比较是字符串的逐位对比:比如"10" < "7"(第一位'1' < '7'),"2" < "7",但"8" > "7"。我们需要统计所有正整数x∈[1,n],满足str(x) < str(num)。
核心状态设计:
pos:当前处理到数字的第几位tight:是否受n的位数限制(即前面的位和n的前缀完全相同,当前位不能超过n对应位)prefix_match:是否和num的前缀完全匹配(当前面的位和num的前缀完全相同时,当前位必须小于num对应位才算字典序小;如果已不匹配,只要满足tight约束即可)leading_zero:是否还在前置零状态(跳过0,只统计正整数)
修正后的Python代码
from functools import lru_cache def count_lex_less(n, num): n_str = str(n) num_str = str(num) len_n = len(n_str) len_num = len(num_str) @lru_cache(maxsize=None) def dp(pos, tight, prefix_match, leading_zero): if pos == len_n: # 排除全零的情况,只统计正整数 return 0 if leading_zero else 1 limit = int(n_str[pos]) if tight else 9 total = 0 for d in range(0, limit + 1): new_tight = tight and (d == limit) new_leading_zero = leading_zero and (d == 0) if new_leading_zero: # 仍处于前置零状态,继续递归 total += dp(pos + 1, new_tight, prefix_match, new_leading_zero) continue # 处理非零数字的情况 if prefix_match: if pos < len_num: num_digit = int(num_str[pos]) if d < num_digit: # 当前位小于num对应位,后续所有符合tight约束的数都合法 total += dp(pos + 1, new_tight, False, new_leading_zero) elif d == num_digit: # 继续匹配num的前缀 total += dp(pos + 1, new_tight, True, new_leading_zero) # d > num_digit时字典序更大,直接跳过 else: # 数字长度超过num且前缀完全匹配,字典序必然更大,跳过 continue else: # 前缀已不匹配,只要符合n的约束就是合法数 total += dp(pos + 1, new_tight, False, new_leading_zero) return total # 处理num大于n的特殊情况 if num > n: if str(n) < str(num): return n else: return count_lex_less(n, num) result = dp(0, True, True, True) return result # 测试用例验证 print(count_lex_less(13, 7)) # 输出10,符合预期
代码关键逻辑说明
- 状态约束:
tight确保生成的数字不会超过n的数值范围prefix_match精准控制字典序的逐位匹配规则,只有前缀完全一致时才需要限制当前位大小leading_zero过滤掉无效的全零情况,只统计正整数
- 递归分支:
- 前置零状态下继续递归,直到选到非零数字
- 前缀匹配时,按当前位与num对应位的大小关系分情况处理
- 前缀不匹配时,只要符合n的约束,所有正整数都计入统计
- 特殊情况处理:当num大于n时,直接判断n的字符串是否小于num的字符串,快速返回结果
内容的提问来源于stack exchange,提问作者S10000
相关产品推荐
相关产品推荐

