在摩尔斯电码中,由分隔符分隔的1-4组中有点和破折号。每组意味着一个字母。在单词之间有两个分隔符。在句子三之间。
解密基本摩尔斯电码的申请非常容易。但我的问题是,如果没有分隔符,如何解决问题呢?我知道会有大量无意义的结果,但这不是我的观点。我只需要以最有效的方式获得所有可能的结果。
这将是一个输入:
......-...-..---
这将是许多产出之一:
hello
你会怎么做?
答案 0 :(得分:3)
阅读dit或dah后,您有两种选择:终止该字母或继续当前字母。这将导致代码中存在大量分歧,并且递归方法可能是实现此目的的好方法。
到目前为止保留一个可能的字符串的缓冲区,并在你点击字符串结尾时打印(或存储)结果,并且它与字母的结尾一致。
这是C:
中的一个实现#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static const char *letter = "**ETIANMSURWDKGOHVF*L*PJBXCYZQ**";
void morse_r(char *buf, int len, const char *str, int code)
{
if (*str == '\0') {
// end of string; print if staring new letter
if (code == 1) printf("%.*s\n", len, buf);
} else if (code < 16) {
if (*str == '.') code = 2 * code;
if (*str == '-') code = 2 * code + 1;
// continue letter
morse_r(buf, len, str + 1, code);
// start new letter
buf[len++] = letter[code];
morse_r(buf, len, str + 1, 1);
}
}
void morse(const char *str)
{
char buf[strlen(str)];
morse_r(buf, 0, str, 1);
}
int main()
{
morse("......-...-..---");
return 0;
}
这个实现非常简单。它使用简单的查找机制,它不会检查字母是否确实存在。 (letter
数组中的星号是有效的莫尔斯码,但它们不是拉丁字母。)
这种方法也相当暴力:它一遍又一遍地重新计算尾巴。尾部的记忆将为处理器的孤独字符串节省大量额外的工作。
而且,正如您所知,会有一些无意义的结果。上面的代码产生20569个字符串(其中一些带有星号,即无效)。当您在路上进行合理性或字典检查时,可以防止多次递归。例如,一行中的许多点将产生大量具有重复Es的无意义词。
编辑正如Jim Mischel指出的那样,莫尔斯代码查找工作原理的解释是有序的。 Yves Daoust在他的回答中提到了一个特里。 trie是用于存储单词的树结构;每个节点可以拥有与字母表中的字母一样多的子节点。摩尔斯电码只有两个字母:dit(.
)和dah(-
)。因此,摩尔斯电码特里是一个二叉树。
尝试通常是稀疏的;单词很长,很多字母组合都不存在。莫尔斯尝试是密集的:莫尔斯字母编码很短,几乎每个cobmination都使用。树可以存储为线性,&#34;平面&#34;数组,类似于heap。节点由数组中的索引i
表示。左边的孩子是2*i
,右边的孩子是2*i + 1
。
在另一个与莫尔斯相关的问题的answer I posted中可以找到更好更详细的解释,我从上面的示例中使用了查找代码。
答案 1 :(得分:2)
IMO最有效的方法是使用trie。这是一棵树,每个节点最多有两个儿子,一个用于.
,一个用于-
,这些字符在给定阶段是可能的。除了指向子的链接之外,节点还有一个“终端”字符,告诉从根到该节点的路径编码的字符是什么。终端字符可以是零,表示路径不对任何字符进行编码(字符串未完成)。
由于莫尔斯字母很小,你甚至可以手工制作特里。这是其中的一部分。
. => E
. => I
. => S
- => U
- => A
. => R
- => W
- => T
. => N
. => D
- => K
- => M
. => G
- => O
要利用trie,请编写一个递归函数,该函数将输入流中的位置和trie中的节点作为输入。如果节点具有终端字符,则将终端字符附加到输出字符串,并将节点重置为trie的根。同时,通过跟随匹配下一个输入符号的儿子继续探索trie。
以下是示例中递归执行的几个第一步(分析前四个输入符号):
. => E
.|. => EE
.|.|. => EEE
.|.|.|. => EEEE
.|.|.. => EEI
.|.. => EI
.|..|. => EIE
.|... => ES
.. => I
..|. => IE
..|.|. => IEE
..|.. => II
... => S
...|. => SE
.... => H
答案 2 :(得分:0)
你可以在2次传球中完成。首先是标记字母结束的位置,第二个标记将提取所有可能的字符串。
您可以将第一个通道实现为动态编程。如果可以将第一个possible[x]
字母解析为某些字符,则x
为真。您使用possible[0] = true
进行调查,然后计算x
的所有其他possible
值。要计算它,你需要最后1,2,3和4个字符,如果它们匹配一些现有的莫尔斯码,并且与字符串其余部分相对应的可能值也是真实的,而且标记possible[x]
也是真的。这是O(N)。
现在你必须提取所有可能的单词。所以,从最后开始,使用possible
向量来消除错误的解决方案。在这里你应该再次尝试最后1-4个字符,看看它们是否匹配,如果它们确实相应的possible
位置为真,那么你将它作为一个可能的字符,并递归调用该函数来生成所有的解决方案。剩下什么。不幸的是,在最坏的情况下(当分区是可能的时候)是指数O(4 ^ N)。在实践中,这将贯穿与输入字符串匹配的可能单词的数量,因此如果只有几个选项,则此过程也将很快。
要注意,字符串越长,您就越有可能获得更多选项和更多可能的解释。
如果另外将可能的单词限制为预定义的集合,则可以修改第一遍以使用单词而不是单个字母。那么可能的解释数量应该减少很多,即使在较长的字符串上你的算法也会很快。