莫尔斯解码高尔夫


24

我已经变得越来越警觉的空间仇这个答案一直激励着我,以确保莫尔斯电码是从这个阴险去除空白的安全。

因此,您的任务是创建一个程序,该程序可以在删除所有空格的情况下成功翻译摩尔斯电码。

摩尔斯电码

规则:

  1. 输入将是仅由破折号和点组成的字符串(ASCII 2D和2E)。对于包含任何其他字符的输入,输出是未定义的。随意使用任何适合您选择的语言的方法来接收输入(stdin,文本文件,提示用户等)。您可以假设摩尔斯电码输入仅包含字母AZ,并且不需要匹配的数字或标点符号。

  2. 输出应仅包含此词典文件中包含的单词(再次,可以使用任何方便的方法访问词典文件)。所有有效的解码都应输出到stdout,并且必须使用输入中的所有点和破折号。输出中每个匹配的单词都应以空格分隔,每个可能的解码均应以换行符分隔。您可以方便地使用大写,小写或大小写混合的输出。

  3. 如上所述,对标准漏洞的所有限制都适用,但有一个例外,如果您确实愿意,可以通过Internet连接访问要求2中引用的词典文件。URL缩短是可以接受的,我认为goo.gl/46I35Z可能是最短的。

  4. 这是代码高尔夫球,最短的代码获胜。

注意:将字典文件发布到Pastebin上后,所有行尾都更改为Windows样式0A 0E序列。您的程序可以假定行尾仅以0A,仅0E或0A 0E结尾。

测试用例:

输入:

......-...-..---.-----.-..-..- ..

输出必须包含:

你好,世界

输入:

.--..-.---------..-..-----..-...--...---..--...-.... ...--.-..-.-.----......--.---.-....-

输出必须包含:

编程难题和代码高尔夫

输入:

-.....--....-..-.-.-.--....-.---.---...-.----..-.- --..---.--....---...-..-.-......-...---..-.---..-- ---。

输出必须包含:

敏捷的棕色狐狸跳过了懒狗


3
您如何分辨AN (.- -.)和之间EG (. --.)
seequ 2014年

2
@Sieg-输出将需要包括两个有效解码。
Comintern

1
@Dennis-啊...我敢打赌,Pastebin或我的浏览器都做到了。我的源文件没有它们。您可以将行定界符更改为一个合适的系统,没有其他更改。当我不在手机上时,我将编辑该问题。
Comintern

2
@Falko这是正确的行为。请注意,问题表明您的输出必须包含 “ hello world”,而不仅限于此。它应该打印所有有效的解码。
hobbs

2
(几乎是充满诗意的)
霍布斯

Answers:


5

红宝石210

(1..(g=gets).size).map{|x|puts IO.read(?d).split.repeated_permutation(x).select{|p|p.join.gsub(/./,Hash[(?a..?z).zip"(;=/%513':07*)29@-+&,4.<>?".bytes.map{|b|('%b'%(b-35))[1,7].tr'01','.-'}])==g}.map{|r|r*' '}}

如果存在“打高尔夫球”这样的习惯,我怀疑这次我参加了。此解决方案生成一个数组,该数组包含从长度1到输入长度的所有字典单词的重复排列。假设“ a”是字典文件中最短的单词,并且其代码是两个字符长,则足以生成长度不超过输入大小一半的排列,但是/2在这个域中添加等同于冗长,所以我避免了。

一旦生成了排列数组(注意:在语法示例输入的情况下,长度为45404 104),则将每个排列数组连接起来,并通过相当方便的(Regexp, Hash)变体将其字母字符替换为它们的摩尔斯电码。#gsub方法; 如果此字符串等于输入,我们发现有效解码。

从名为“ d”的文件中读取(多次)该词典,并且输入中不得包含换行符。

运行示例(使用字典,该字典将使程序在宇宙热死之前结束时有战斗的机会):

$ cat d
puzzles
and
code
dummy
golf
programming
$ echo -n .--..-.-----..-..-----..-.--..--...---..--...-.......--.-..-.-.----...--.---.-....-. | ruby morse.rb
programming puzzles and code golf
^C

5

Haskell,296个字符

  • 字典文件:必须是名为“ d”的文本文件
  • 输入:stdin,可能带有结尾的换行符,但内部没有空格
main=do f<-readFile"d";getLine>>=mapM(putStrLn.unwords).(words f&)
i!""=[i]
(i:j)!(p:q)|i==p=j!q
_!_=[]
_&""=[[]]
d&i=do
w<-d
j<-i!(w>>=((replicate 97"X"++words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..")!!).fromEnum)
n<-d&j
[w:n]

元素说明:

  • main读取字典,读取stdin,执行&&使用适当的空格格式化其输出。
  • (replicate 97"X"++words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..")!!)(的定义内部的表达式&)是一个列表,其索引是字符代码(97是的代码'a'),值是莫尔斯序列。
  • !(名为中缀运算符的函数)将字符串与前缀匹配;如果存在前缀,则它将返回一个元素列表中的其余部分(列表monad中的成功),否则返回空列表(列表monad中的失败)
  • &使用列表monad进行“不确定”执行;它

    1. 选择d(字典词)的条目,
    2. 用于!将该词的Morse形式(w>>=((…)!!).fromEnum等效于concatMap (((…)!!) . fromEnum) w)与输入字符串进行匹配i
    3. 调用自身(d&j)以匹配其余字符串,并且
    4. w:n在列表monad中返回可能的结果,作为单词列表,[w:n](更短,具体等效于return (w:n))。

    请注意,第6行之后的每一行都是do从第6行开始的表达式的一部分;这与单行上使用分号的字符数完全相同,但是可读性更高,尽管您只能在程序中执行一次。

这个程序非常慢。通过将混杂词存储在原始词旁边的列表中,而不必在每次模式匹配时重新计算它们,就可以轻松地使其变得更快(且稍长一些)。接下来要做的是将单词存储在由莫尔斯符号(2-ary trie)作为键的二叉树中,以避免尝试不必要的分支。

如果字典文件中不包含未使用的符号(例如“-”),则可以使其稍短一些,从而允许删除,replicate 97"X"++而建议.(-97+)!!。之前执行。


该死的儿子,这很聪明。+1给你。
亚历山大·克拉格斯

1
您知道(+(-97))可以重写为(-97+)吗?
自豪的haskeller 2014年

您应该删除h的第三个定义,而应添加|0<1=[]到第二个定义
骄傲的haskeller 2014年

2
使用interact并赢得12个字符。interact$unlines.map unwords.(words f&)
gxtaillon

1
您应该可以替换concatMap>>=
骄傲的haskeller 2014年

3

蟒蛇- 363 345

码:

D,P='-.';U,N='-.-','.-.'
def s(b,i):
 if i=='':print b
 for w in open('d').read().split():
  C=''.join([dict(zip('abcdefghijklmnopqrstuvwxyz-\'23',[P+D,D+3*P,U+P,'-..',P,D+N,'--.',4*P,2*P,P+3*D,U,N+P,2*D,D+P,D*3,'.--.',D+U,N,P*3,D,'..-',3*P+D,'.--','-..-',U+D,'--..']+['']*4))[c]for c in w]);L=len(C)
  if i[:L]==C:s(b+' '+w,i[L:])
s('',input())

说明:

词典必须存储为名为“ d”的纯文本文件。

DPUN只是对莫尔斯查找表的定义更短一些辅助变量。

s(i)是一个递归函数,它打印先前翻译的消息部分p和其余代码部分的每个有效翻译i:如果i为空,则到达代码的末尾,其中b包含整个翻译,因此我们将其简单化print。否则,我们检查w字典中的每个单词d,将其翻译为莫尔斯电码C,如果其余代码i以开头C,则将单词添加w到翻译后的开头,b然后s对其余单词递归调用该函数。

关于效率的注意事项:

这是一个很慢但打高尔夫球的版本。尤其是dict(zip(...))在每次迭代中加载字典并构造莫尔斯(Morse)查找表()(以避免更多变量)会花费很多。提前翻译一次词典文件中的所有单词,而不是按需进行每次递归翻译,会更有效。这些想法导致以下版本增加了40个字符,但速度明显提高:

d=open('d').read().split()
D,P='-.';U,N='-.-','.-.'
M=dict(zip('abcdefghijklmnopqrstuvwxyz-\'23',[P+D,D+3*P,U+P,'-..',P,D+N,'--.',4*P,2*P,P+3*D,U,N+P,2*D,D+P,D*3,'.--.',D+U,N,P*3,D,'..-',3*P+D,'.--','-..-',U+D,'--..']+['']*4))
T=[''.join([M[c]for c in w])for w in d]
def s(b,i):
 if i=='':print b
 for j,w in enumerate(d):
  C=T[j];L=len(C)
  if i[:L]==C:s(b+' '+w,i[L:])
s('',input())

您可以将替换.startswith(C)为来保存2个字符[:len(C)]==C
Greg Hewgill 2014年

哇谢谢!这变得很奇怪,因为在每次递归中加载整个词典可以节省字符,并再次降低算法的速度。
Falko 2014年

@GregHewgill:是的,那是我最初所做的。我刚刚编辑了答案以解决这两个版本。
Falko 2014年

1
通过按单词长度的降序对字典进行排序,可以更快地产生更有趣的结果(更长的单词)。d=sorted(open('d').read().split(),key=len,reverse=1)或者,通过以这种方式对字典进行预排序从外部进行操作。
格雷格·希吉尔

哎呀,如果您可以重新格式化字典文件,请将其格式化为预先计算的Python字典,然后M=eval(open('d').read()):)
Greg Hewgill 2014年

3

Perl(5.10 +),293个字符

字典文件应另存为“ d”(如果您不希望单词之间使用CR,则应以unix格式保存),在stdin上输入莫尔斯电码,并且不包含尾随换行符(请使用echo -n)。

open D,d;chomp(@w=<D>);@m{a..z}=map{substr(sprintf("%b",-61+ord),1)=~y/01/.-/r}
'BUWI?OKMATJQDCLSZGE@FNHVXY'=~/./g;%w=map{$_,qr#^\Q@{[s/./$m{$&}/reg]}#}@w;
@a=[$i=<>];while(@a){say join$",@{$$_[1]}for grep!$$_[0],@a;
@a=map{$x=$_;map{$$x[0]=~$w{$_}?[substr($$x[0],$+[0]),[@{$$x[1]},$_]]:()}@w}@a}

(仅换行符用于格式化)。

取消程式码:

# Read the word list
open my $dictionary, '<', 'd';
chomp(my @words = <$dictionary>);

# Define morse characters
my %morse;
@morse{'a' .. 'z'} = map {
  $n = ord($_) - 61;
  $bits = sprintf "%b", $n;
  $bits =~ tr/01/.-/;
  substr $bits, 1;
} split //, 'BUWI?OKMATJQDCLSZGE@FNHVXY';

# Make a hash of words to regexes that match their morse representation
my %morse_words = map {
  my $morse_word = s/./$morse{$_}/reg;
  ($_ => qr/^\Q$morse_word/)
} @words;

# Read the input
my $input = <>;

# Initialize the state
my @candidates = ({ remaining => $input, words => [] });

while (@candidates) {
  # Print matches
  for my $solution (grep { $_->{remaining} eq '' } @candidates) {
    say join " ", @{ $solution->{words} }; 
  } 
  # Generate new solutions
  @candidates = map {
    my $candidate = $_;
    map {
      $candidate->{remaining} =~ $morse_words{$_}
        ? {
          remaining => substr( $candidate->{remaining}, $+[0] ),
          words => [ @{ $candidate->{words} }, $_ ],
        }
        : ()
    } @words
  } @candidates;
}

操作方式:

通过更改“。”来存储摩尔斯电码符号。和“-”分别转换为二进制数字0和1,并在其前面加上“ 1”(以免前导点被吞噬),将二进制数字转换为十进制,然后将字符编码为更高的61可打印的字符,不需要反斜杠)。

我认为这是一种分区问题,并以此为基础构建了解决方案。对于字典中的每个单词,它都会构造一个正则表达式对象,该对象将匹配(并捕获)该单词在字符串开头的无空间莫尔斯表达。然后,它通过创建一个没有单词匹配且整个输入都为“剩余输入”的状态,开始进行广度优先搜索。然后,它通过查找在其余输入的开头匹配的单词,并创建将单词添加到匹配的单词并从其余输入中删除莫尔斯词的新状态,来扩展每个状态。没有剩余输入的国家将成功,并打印其单词列表。无法匹配任何单词的状态(包括成功的单词)不会产生子状态。

请注意,在非高尔夫版本中,状态是哈希值,以提高可读性。在高尔夫球版中,它们是数组(用于缩短代码并减少内存消耗);slot [0]是剩余的输入,slot [1]是匹配的单词。

评论

这太慢了。我想知道是否有解决方案。我尝试使用Marpa(一个Earley解析器,能够为单个输入字符串提供多个解析)来构建一个,但仅在构造语法时就用光了内存。也许如果我使用较低级别的API而不是BNF输入...


如果添加与Kevin Reid相同的要求(输入中没有换行符),则可以通过删除来保存7个字符chomp()。我是不是该?
hobbs

“随时使用任何方便的方法”。
Comintern

ord代替2个字节ord$_。刮胡子要说一个字节join$"而不是join" "
Zaid 2014年

2

哈斯克尔-418

通过动态编程可以有效地解决该装饰问题。我知道这是一个代码高尔夫,但是我喜欢快速代码。

假设我们有输入字符串s,然后构建一个数组dp,它dp[i]是子字符串的所有有效解码结果的列表s[:i]。对于每一个单词w在字典中,我们首先它编码为mw,那么我们就可以计算的一部分dp[i]来自dp[i - length(mw)]如果s[i - length(mw):i] == mw。建筑的时间复杂度dpO({count of words} {length of s} {max word length})。最后,dp[length(s)]最后一个元素是我们需要的。

实际上,我们不需要将整个解码存储为each的元素dp[i]。我们需要的是最后一个解码的单词。这使实现速度大大加快。在我的i3笔记本电脑上完成“ hello world”保护壳的时间不到2秒。对于问题中发布的其他情况,由于输出太多,该程序将无法正常完成。

使用动态编程技术,我们可以计算有效解码的数量。您可以在此处找到代码。结果:

input: ......-...-..---.-----.-..-..-..
count: 403856

input: .--..-.-----..-..-----..-.--..--...---..--...-.......--.-..-.-.----...--.---.-....-.
count: 2889424682038128

input: -.....--.-..-..-.-.-.--....-.---.---...-.----..-.---..---.--....---...-..-.-......-...---..-.---..-----.
count: 4986181473975221635

不打高尔夫球

import Control.Monad

morseTable :: [(Char, String)]
morseTable = zip ['a'..'z'] $ words ".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.."

wordToMorse :: String -> Maybe String
wordToMorse xs = return . concat =<< mapM (`lookup` morseTable) xs

slice :: (Int, Int) -> [a] -> [a]
slice (start, end) = take (end - start) . drop start

decode :: String -> String -> IO ()
decode dict s = trace (length s) [] where
  dict' = [(w, maybe "x" id . wordToMorse $ w) | w <- lines dict]
  dp = flip map [0..length s] $ \i -> [(j, w) |
        (w, mw) <- dict', let j = i - length mw, j >= 0 && mw == slice (j, i) s]

  trace :: Int -> [String] -> IO ()
  trace 0 prefix = putStrLn . unwords $ prefix
  trace i prefix = sequence_ [trace j (w:prefix) | (j, w) <- dp !! i]

main :: IO ()
main = do
  ws <- readFile "wordlist.txt"
  decode ws =<< getLine

打高尔夫球

import Control.Monad
l=length
t=zip['a'..]$words".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.."
h s=return.concat=<<mapM(`lookup`t)s
f d s=g(l s)[]where g 0 p=putStrLn.unwords$p;g i p=sequence_[g j(w:p)|(j,w)<-map(\i->[(j,w)|(w,m)<-[(w,maybe"x"id.h$w)|w<-lines d],let j=i-l m,j>=0&&m==(take(i-j).drop j$s)])[0..l s]!!i]
main=do d<-readFile"d";f d=<<getLine

很高兴看到合理有效的解决方案。现在我只需要了解它:)
hobbs

2

PHP,234226字节

function f($s,$r=""){$s?:print"$r
";foreach(file(d)as$w){for($i=+$m="";$p=@strpos(__etianmsurwdkgohvf_l_pjbxcyzq,$w[$i++]);)$m.=strtr(substr(decbin($p),1),10,"-.");0!==strpos($s,$m)?:g(substr($s,strlen($m)),$r.trim($w)." ");}}

递归函数,从名为的文件中获取字典d
字典中包含非字母的每个单词均失败。

define ("d","<filename>");调用函数之前,可以使用任何文件名。

添加2或3个字节以加快执行速度:
删除并在之前和之前 $s?:print"$r\n";插入。$s!=$m?0!==:print$r.$w;}}

分解

function f($s,$r="")
{
    $s?:print"$r\n";            // if input is empty, print result
    foreach(file(d)as$w)        // loop through words
    {
        // translate to morse:
        for($i=+$m="";              // init morse to empty string, $i to 0
                                        // loop while character is in the string
            $p=@strpos(__etianmsurwdkgohvf_l_pjbxcyzq,$w[$i++])
        ;)
            $m.=                        // 4. append to word morse code
                strtr(
                    substr(
                        decbin($p)      // 1: convert position to base 2
                    ,1)                 // 2: substr: remove leading `1`
                ,10,"-.")               // 3. strtr: dot for 0, dash for 1
            ;
        0!==strpos($s,$m)           // if $s starts with $m
            ?:f(                        // recurse
                substr($s,strlen($m)),  // with rest of $s as input
                $r.trim($w)." "         // and $r + word + space as result
            )
        ;
    }
}

1

Groovy的377 337

r=[:];t={x='',p=''->r[s[0]]=p+x;s=s.substring(1);if(p.size()<3){t('.',p+x);t('-',p+x)}};s='-eishvuf-arl-wpjtndbxkcymgzqo--';t()
m=('d'as File).readLines().groupBy{it.collect{r.get(it,0)}.join()}
f={it,h=[]->it.size().times{i->k=it[0..i]
if(k in m){m[k].each{j->f(it.substring(i+1),h+[j])}}}
if(it.empty){println h.join(' ')}}
f(args[0])

笔记

字典必须是名为的文件d。摩尔斯字符串是通过命令行传递的。例如:

% groovy morse.groovy ......-...-..---.-----.-..-..-.. | grep 'hello world'
hello world

对于“莫尔斯电码压缩”,我使用的是二叉树

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.