变位词求解

把词库里每个词按字母升序排成签名,再把目标词归一到同一个签名,就能一次性列出所有互为变位词的词,并继续找出两个词、三个词拼成的组合。

求解规则
目标签名eilnst
目标字母数6
可用词29 个
命中结果7 条
#单词组合词数字母数
1enlist16
2inlets16
3silent16
4tinsel16
5in + lets26
6is + lent26
7it + lens26

提示:目标是单词时,输入框里按 Enter 直接求解;词库是文本框,Enter 留给换行,请按 Ctrl + Enter(macOS 用 Cmd + Enter)求解。上面的结果就是打开页面时的默认示例,点「开始求解」可随时重算。

使用方法

  1. 在「目标单词或短语」里填 listen(页面默认示例)。只有 A–Z 字母进入计算,空格、连字符、数字、标点都会被忽略,大小写也不区分——所以 Listen 与 listen 的签名完全相同。
  2. 在「词库」里每行放一个英文词,默认预置了 32 个词。也可以直接把一整段词表粘进去,工具会按「非字母字符」切分,空格、逗号、换行、制表符都能当分隔符。
  3. 「最短词长」默认 2,含义是参与匹配的词至少两个字母。这一步同时过滤词库和结果:1 个字母的 a、i 会被挡在外面。
  4. 「最多组合词数」默认 2:既能找出单词变位词,也能找出两个词拼成的组合。设成 1 就只看单词;设成 3、4 会继续尝试三词、四词组合。
  5. 「必须用完全部字母」默认勾选。勾上时结果必须恰好使用目标的全部字母;取消勾选则放宽为「这些字母能拼出的词」,会顺带把 in、is 这类短词也列出来。
  6. 点「开始求解」,变位词求解会把结果写进下方表格:默认显示 12 行,超出后出现「加载更多」;「复制对照表」把整张表以制表符(TSV)复制到剪贴板,「导出 CSV」下载带 BOM 的表格文件,Excel 打开不会乱码。

这个变位词求解能解决什么问题

一是查同一组字母能拼出的所有单词。拿到 listen 这 6 个字母,人脑很难穷举出 silent、enlist、inlets、tinsel 这四兄弟,而这个变位词求解在本地一次排序就全部列出来。

二是验证两个词是不是变位词。把其中一个词填进目标框、另一个词放进词库,命中了就是。判断依据是字母多重集是否相同,和词义、词性、发音都无关。

三是拼字类游戏的助手。拼字游戏、填字游戏、Hangman 里经常需要「用给定的几个字母凑出合法词」,把游戏词库粘进来、把手里字母填进目标框即可按长度筛。

四是短语变位词与文字游戏。把「最多组合词数」调到 3 或 4,可以试着拆解 eleven plus two 与 twelve plus one 这类短语变位词——两者签名都是 eeellnopstuvw。

本页不做这些事:不做发音相近检索(那是押韵工具的事)、不做词典释义、不做跨语言翻译、不做词形还原(run 和 running 不会被当成同一个词)。要查押韵词请用页面底部的「押韵词查询」,要按游戏词典校验拼写请用「拼字游戏词查询」。

求解原理:变位词求解的排序签名与字母多重集剪枝

核心只有一行:sig(w) = [...w.toLowerCase()].filter(c => c >= 'a' && c <= 'z').sort().join('')。把一个词的所有字母取出来、按 a 到 z 升序重新排列,得到的字符串就是这个词的签名。例如 listen 排完是 eilnst,silent 排完也是 eilnst。

为什么签名相同就等价于互为变位词?因为两个词是变位词的定义就是「每个字母出现的次数完全相同」。对字母做一次稳定排序,等于先按字母分桶、再按 a–z 顺序拼接;只要两张字母频次表逐项相等,拼出来的字符串必然逐位相等。反过来,签名逐位相等也意味着频次表逐项相等。所以「排序后字符串相等」是变位词关系的充要条件,不是近似判断。这个做法本质上是计数排序的一个特例,比较的是 26 个字母桶的计数。

为什么不用全排列?6 个互不相同的字母有 6! = 720 种排列,逐个生成再去词库里查,等于把每个词展开成 720 次比较。签名法换了个方向:词库里每个词各排序一次建表,查询时只算一次目标签名再查表。词库 100 个词是 100 次排序,10000 个词是 10000 次排序,查询始终是一次哈希查找。

多词组合怎么处理。把「签名」从单个词推广到一组词:若干个词拼在一起的签名,就是它们全部字母一起排序。于是枚举时用一个字母多重集剪枝——每加入一个词,先检查累计字母是否仍然「不超过目标字母的库存」:目标 listen 只有一个 l、一个 n,那么 lentil(两个 l)和 linen(两个 n)在第一步就会被剪掉,根本不会进入后续枚举。默认词库 32 个词里,被这步剪掉的是 stone(含 o)、linen、lentil 这 3 个,剩下 29 个可用词。

关键参数为什么这样取。「最短词长」下限取 2,是因为英文里真正被广泛使用的单字母词只有 a 和 i,放进结果只会稀释有效信息;上限取 10,是因为目标字母上限只有 20 个,词长超过 10 的词在多数查询里根本放不下。「最多组合词数」上限取 4,是因为组合数按 C(n,k) 增长:20 个字母时四词组合的组合上界已达 4845 种,再往上只会让枚举变慢而几乎找不到真结果。

字符处理上,本页只认 ASCII 基本拉丁字母两个区间:大写 U+0041–U+005A、小写 U+0061–U+007A,二者在 UTF-16 里各占 1 个码元。

完整示例(数值仅作演示)

示例条件与页面打开时的默认值完全一致:目标 listen、词库 32 个词、最短词长 2、最多组合词数 2、勾选「必须用完全部字母」。实测过程与结果:

顺手把词库按签名分个组,就能看出「一个签名对应一个变位词家族」:

签名词数家族成员
eilnst5enlist、inlets、listen、silent、tinsel
ilst3list、silt、slit
eils2isle、lies
eist2site、ties
ent2net、ten

输入范围与对照表

四个可控参数的范围与越界后的表现:

下表的「组合上界」按一个宽松假设给出:目标有 n 个字母,可用词池最多 n 个,于是两词组合最多 C(n,2)、三词最多 C(n,3)。实际因为有字母多重集剪枝,枚举量远小于这个上界(示例里 29 个可用词、双词上界 406,实测只命中 3 条)。

目标字母数全排列双词组合上界三词组合上界四词组合上界
6(默认示例)720152015
840320285670
10362880045120210
124.79×10866220495
162.09×10131205601820
20(上限)2.43×101819011404845

这张表解释了为什么要把目标限制在 20 个字母:8 个字母的全排列已经是 40320 种,12 个字母逼近 4.79 亿——暴力枚举字符串的思路早就不可行了,签名法的价值正体现在这里。

结果边界与精度说明

下面几条不是「限制」,而是刻意设定的边界,知道了就不会把结果用错:

常见问题

结果里为什么找不到目标词本身?

因为工具会主动剔除原词。目标是 listen 时,词库里的 listen 会被跳过,剩下 4 个单词结果。这样设计是因为几乎没有人想看到「输入什么就回什么」。想反过来看到它,把目标换成 silent,结果里就会出现 listen。

词库里的 stone、linen 为什么一条都没出?

它们的字母超出了目标的库存。stone 含 o 而目标是 listen 不含 o;linen 有两个 n、lentil 有两个 l,而目标各只有一个。字母多重集剪枝在加入这个词的第一步就判定"超量",直接跳过,所以它们连一次排序都不会参与。

怎么手工验证两个词互为变位词?

各自把字母排序再比较即可。listen → eilnst,silent → eilnst,两个字符串完全一样,所以互为变位词;list → ilst 与 listen → eilnst 长度都不同,显然不是。注意排序前要统一小写,并且只保留 a–z 字母。

不勾「必须用完全部字母」会变成什么样?

判定条件从「签名完全相等」放宽成「字母是该签名的子多重集」,相当于从"找出全部字母的排列"变成"这些字母能拼出哪些词"。示例里 32 词词库下会多出 in、is、it、line、list 等大量短词。做拼字游戏时这个模式更实用,做「严格变位词」时记得勾回去。

eleven plus two 和 twelve plus one 为什么能互相转换?

因为空格被忽略后,两串字母完全相同:e 3 个、l 2 个,v n p u s t w o 各 1 个,签名都是 eeellnopstuvw。把目标的「最多组合词数」设为 3,词库里同时放上 eleven、twelve、plus、two、one,就能看到两组词互相命中。

词库要放多大才够用?会不会卡?

本页预置的 32 词只是演示用的迷你词库,真实需求建议粘一份两万词左右的常用词表。建表成本是「每个词排一次序」,两万词在普通笔记本上是毫秒级;查询本身是一次哈希查找。真正限制速度的是「最多组合词数」,它决定枚举量,而不是词库大小。

为什么最短词长的下限是 2 而不是 1?

英文里被广泛使用的单字母词只有 a 和 i。把它们放进来,任何含这两个字母的目标都会多出「x + a」「i + y」这类没有信息量的组合,而真正的变位词会被淹没。下限设 2 是精度与噪声之间的取舍,如果你确实要处理缩写表,可以自己把单字母词从词库里删掉。

支持中文、日文或带重音的欧洲语言吗?

不支持。签名阶段只保留 ASCII 的 U+0041–U+005A 与 U+0061–U+007A,其余字符一律丢弃。中文没有"字母多重集相同"这种关系——汉字是语素文字,不是拼音文字,强行按码位排序没有语言学意义。日文假名、希腊字母、西里尔字母同样不在处理范围内。

相关工具