换词器可达性


13

换词器是一款您试图通过单字符编辑将一个单词变成另一个单词的游戏,每个步骤都是自己的单词。对于这个挑战,编辑可能是替换,插入或删除。例如,WINNER→LOSER可以使用以下路线来完成(可能还有其他路线):

WINNER
DINNER
DINER
DINE
LINE
LONE
LOSE
LOSER

换一种说法,您必须每次只能在Levenshtein距离为1的情况下通过另一个单词到达另一个单词。

编码

您将得到一个单词列表和两个单词,如果存在一条路由,则必须输出从一个单词到另一个单词的有效路由;如果不存在任何路由,则必须输出一个不同的常数值或一致的行为。

  • 您可以假设输入的单词都在单词列表中
  • 单词列表可以通过任何方便的平面格式输入。
    • 列表,集合,尝试,空格分隔的字符串和行分隔的文件都是有效的(例如),但Levenshtein邻接关系的预先计算的图形无效。
  • 输出路径应该包括两个输入词,但是开始和结束都没有关系。
  • 如果未找到路由,则可以输出特定的常量,错误的值,空列表,引发异常,使用非零代码退出或在限定时间内发生的任何其他行为。
  • 路线不一定是最佳路线,也没有要求应该采用哪条路线
  • 计算复杂性无关紧要,但是必须证明您的程序在有限的时间内终止。(即使它超越了宇宙的热死)
  • 您可以假设所有单词在相同情况下完全由字母组成

示例测试用例

  • 猫→狗; [CAT,DOG,COG,COT,FROG,GROG,BOG]
    • 猫,狗,狗,狗
  • 浴室→淋浴; [沐浴,淋浴,卫生,帽子,BAT,SAT,锯,母猪,显示,如何]
    • 找不到路线
  • BREAK→FIX;[BREAK,FIX,BEAK,BREAD,READ,BEAD,RED,BED,BAD,BID,FAD,FAX]
    • BREAK,BREAD,BEAD,BAD,FAD,FAX,FIX
  • 建造→毁灭; [建造,毁灭,建造,公会,公会,吉尔,吉尔,比尔,莳萝,填充,破坏,结构,构造]
    • 找不到路线
  • 卡→板; [卡,板,板]
    • 卡,板,板
  • 恶魔→天使; [天使魔鬼]
    • 找不到路线
  • 最后→过去;[最后,过去,爆炸,投射,黑色,幽灵,后期,夸张]
    • 最后,过去
  • 插入→删除; 这个单词表
    • 插入,倒置,侵入,弯曲,弯曲,弯曲,弯曲,变暗,达林,成骨,成骨,成丝,成串,丝氨酸,成年神经,有色,硅酸盐,硅酸盐,陶土,降级,删除


1
我们可以输出有效路线列表还是应该是一条路线?
Emigna

@Emigna任何路线都可以。正如我提到的那样,“路线不必是最优的”
Beefster

我们是否需要在输出中包含开始和结束词?路线将始终以相同的起点和终点开始!
魔术章鱼缸

1
“ @MagicOctopusUrn”输出路径应该同时包含两个输入词,但是开始和结束无关紧要。
Beefster '19

Answers:


5

05AB1E23 21 20字节

打印有效路线列表。
感谢Kevin Cruijssen保存了2个字节。

怜€`ʒü.LP}ʒ¬²Qsθ³Q*

在线尝试!


更改Dævyœ«}为可以节省2个字节怜€` 。(不知道为什么两个映射都可以单独使用,但æεœ`}不能顺便说一句,但是无论如何它都是相同的字节数。)
Kevin Cruijssen

太糟糕的产品[]1不是0(不要太奇怪,虽然)或平等的检查与空列表显然会导致一个空列表,而不是0(这一个我看到的一个bug ..)..否则,你可能会合并过滤器和find_first来保存另一个字节:怜€`.Δü.LPy¬²Qsθ³QP
Kevin Cruijssen

@KevinCruijssen:谢谢!不知道为什么我没有想到使用。我认为由于矢量化,相等的检查结果会导致列表空白。对于空列表,也许应该有一个特殊情况,但是在其他情况下,这可能是意外的。
Emigna

1
在17岁时是否可以执行以下操作:在线尝试!
魔术章鱼缸

1
@MagicOctopusUrn:不幸的是,我们需要在输出中包括路径的所有单词。
Emigna

4

JavaScript(V8) 177176 字节

将输入作为(target)(source, list)。打印所有可能的路线。如果没有解决方案,则不打印任何内容。

t=>F=(s,l,p=[],d)=>s==t?print(p):l.map((S,i)=>(g=(m,n)=>m*n?1+Math.min(g(m-1,n),g(m,--n),g(--m,n)-(S[m]==s[n])):m+n)(S.length,s.length)^d||F(S,L=[...l],[...p,L.splice(i,1)],1))

在线尝试!

已评论

t =>                            // t = target string
F = (                           // F is a recursive function taking:
  s,                            //   s = source string
  l,                            //   l[] = list of words
  p = [],                       //   p[] = path
  d                             //   d = expected Levenshtein distance between s and the
) =>                            //       next word (initially undefined, so coerced to 0)
  s == t ?                      // if s is equal to t:
    print(p)                    //   stop recursion and print the path
  :                             // else:
    l.map((S, i) =>             //   for each word S at index i in l[]:
      ( g =                     //     g = recursive function computing the Levenshtein
        (m, n) =>               //         distance between S and s
        m * n ?                 //       if both m and n are not equal to 0:
          1 + Math.min(         //         add 1 to the result + the minimum of:
            g(m - 1, n),        //           g(m - 1, n)
            g(m, --n),          //           g(m, n - 1)
            g(--m, n) -         //           g(m - 1, n - 1), minus 1 if ...
            (S[m] == s[n])      //           ... S[m - 1] is equal to s[n - 1]
          )                     //         end of Math.min()
        :                       //       else:
          m + n                 //         return either m or n
      )(S.length, s.length)     //     initial call to g with m = S.length, n = s.length
      ^ d ||                    //     unless the distance is not equal to d,
      F(                        //     do a recursive call to F with:
        S,                      //       the new source string S
        L = [...l],             //       a copy L[] of l[]
        [...p, L.splice(i, 1)], //       the updated path (removes S from L[])
        1                       //       an expected distance of 1
      )                         //     end of recursive call
    )                           //   end of map()


3

Python 2,155个字节

f=lambda a,b,W,r=[]:a==b and r+[a]or reduce(lambda q,w:q or any({a,a[:i]+a[i+1:]}&{w,w[:i]+w[i+1:]}for i in range(len(a+w)))and f(w,b,W-{a},r+[a]),W-{a},0)

在线尝试!

以两个单词和一组单词为输入;如果以字符串列表的形式存在,则返回(非最佳)路由,否则返回False。

该片段:

any({a,a[:i]+a[i+1:]}&{w,w[:i]+w[i+1:]}for i in range(len(a+w)))

True当且仅当a==wa具有Levenshtein距离1w



2

Python 2,163字节

如果找到路由,则将其输出到stderr,程序以退出代码1退出。
如果没有路由,则没有输出,并且程序以退出代码0终止。

s,e,d=input();r=[[s]]
for x in r:t=x[-1];t==e>exit(x);r+=[x+[w]for w in d-set(x)for a,b in(t,w),(w,t)for i in range(len(b)*2)if a==b[:i/2]+a[i/2:][:i%2]+b[i/2+1:]]

在线尝试!


1

Python 3中217个 214 212 201字节

-11字节thanx到xnor的提示

d=lambda a,b:min(d(a[1:],b[1:])+(a[0]!=b[0]),d(a[1:],b)+1,d(a,b[1:])+1)if b>""<a else len(a+b)
def g(a,b,l,p=[]):
	if a==b:yield[a]+p
	for c in(a!=b)*l:
		if(c in p)+d(a,c)==1:yield from g(c,b,l,[a]+p)

在线尝试!


0

果冻,38个字节

⁵ḟ,€0ị$ṭ¹-Ƥ$€e€/ẸƊƇḢ€
Wṭ@ⱮÇßƊe@⁴oṆƲ?€Ẏ

在线尝试!

接受三个参数的完整程序。第一个是起始词,提供为[["START"]]。第二个参数是最终字词,提供为"END"。第三个参数是单词列表,以引号引起来,用逗号分隔。

程序返回一个列表列表,每个列表代表从头到尾的有效路径。如果没有有效的路由,则响应为空列表。

在TIO链接中,有页脚文本,可以很好地显示结果,每个单词用空格分隔,每个单词列表用换行符分隔。如果首选打印输出的基础列表表示形式,则可以按进行ÇŒṘ

与05ABIE不同,Levenshtein距离没有内置的功能,因此该程序比较缺少单个字符的前缀,与@ChasBrown的解决方案有些相似,尽管带有Jelly扭曲。

说明

Helper链接:单子链接,该单词接受单词列表并返回可能的扩展列表的列表;如果无法进一步扩展,则返回空列表

⁵ḟ                      | Filter the word list to remove words already used
  ,€0ị$                 | Pair each word with the last word in the current path
                  ƊƇ    | Filter these pairs such that
              e€/Ẹ      |   there exists any
       ṭ¹-Ƥ$€           |   match between the original words or any outfix with a single character removed
                    Ḣ€  | Take the first word of each of these pairs (i.e. the possible extensions of the route)

主链接

              €         | For each of the current paths
            Ʋ?          | If:
       e@⁴              |   The path contains the end word
          oṆ            |   Or the path is empty (i.e. could not be extended)
W                       | Return the path wrapped in a list (which will be stripped by the final Ẏ)
 ṭ@ⱮÇ                   | Otherwise, look for possible extensions using the helper link, and add onto the end of the path
     ßƊ                 | And then feed all of these paths back through this link
               Ẏ        | Strip back one layer of lists (needed because each recursion nests the path one list deeper)

0

Swift 4.2 / Xcode 10.2.1,387字节

func d(l:String,m:String)->Bool{return (0..<l.count).contains{var c=l;c.remove(at:c.index(c.startIndex,offsetBy:$0));return c==m}};func f(r:[String])->[String]{if b==r.last!{return r};return w.lazy.map{!r.contains($0)&&(d(l:r.last!,m:$0)||d(l:$0,m:r.last!)||(r.last!.count==$0.count&&zip(r.last!,$0).filter{$0 != $1}.count==1)) ? f(r:r+[$0]):[]}.first{!$0.isEmpty} ?? []};return f(r:[a])

在线尝试!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.