让我们暂时假设所有计数都小于或等于某个最大值 M,其中 M < 100。正如 OP 的链接中所述,这意味着我们只需要确定这些数字单词中出现的 16 个字母的计数,因为其他 10 个字母的计数已经由指定的前缀文本确定并且不能更改。
我认为值得利用的一个特性是,如果我们采用一些(可能不正确的)解决方案并重新排列其中的数字单词,那么总字母数不会改变。IOW,如果我们忽略用于“命名自己”的字母(例如c
in two c's
),那么总字母数仅取决于句子中实际出现的数字单词的多重集。这意味着我们不必考虑将 M 个数字词中的一个分配给 16 个字母中的每一个的所有可能方法,我们可以仅枚举所有大小为 16 或更少的数字词多集的(小得多的)集合,从大小为 M 的数字词的基本集合中提取元素,对于每个多重集合,看看我们是否可以拟合其元素的 16 个字母以每个多重集元素仅使用一次的方式。
请注意,一组数字可以唯一地表示为一个非递减的数字列表,这使得它们易于枚举。
一个字母“适合”一个多集是什么意思?假设我们有一个数字词的多重集 W;这确定了 16 个字母中每个字母的总字母数(对于每个字母,只需将该字母在 W 中所有数字单词中的计数相加;此外,还为每个数字单词的字母“S”添加 1 “一”,解释复数)。将这些字母计数 f["A"] 称为“A”的频率等。假设我们有一个函数 etoi(),它的操作类似于 C 的 atoi(),但返回一个数字字的数值。(这只是概念性的;当然在实践中,我们总是会从整数值(我们会保留它)生成数字字,而不是反过来。)然后字母 x 适合特定的数字字 w W 当且仅当 f[x] + 1 = etoi(w),
这还没有解决这样一个事实,即如果多个字母适合一个数字词,则只能分配其中一个。但事实证明,很容易确定给定的数字词多重集 W(表示为整数的非递减列表)是否同时适合任何字母集:
- 计算 W 所暗示的总字母频率 f[]。
- 对这些频率进行排序。
- 跳过任何零频率字母。假设有 k 个。
- 对于剩余的每个字母,检查其频率是否等于相应位置的数字单词的数值小一。即检查 f[k] + 1 == etoi(W[0])、f[k+1] + 1 == etoi(W[1]) 等。
- 当且仅当所有这些频率都一致时,我们才有赢家!
上述方法很幼稚,因为它假设我们从 M 大小的地面集中选择要放入多重集中的单词。对于 M > 20,这个集合中有很多结构可以利用,但代价是算法稍微复杂。特别是,与其枚举所有允许数字的基本集合的直接多重集,不如枚举 {"one", "two", ..., "nineteen", "twenty", "thirty" 的多重集,“四十”,“五十”,“六十”,“七十”,“八十”,“九十”},然后允许“适合检测”步骤将10的倍数的数字单词与个位数组合数词。