莫尔斯没有分离器 - 最好的算法

时间:2016-01-25 06:24:37

标签: algorithm cryptography processing-efficiency morse-code

在摩尔斯电码中,由分隔符分隔的1-4组中有破折号。每组意味着一个字母。在单词之间有两个分隔符。在句子三之间。

解密基本摩尔斯电码的申请非常容易。但我的问题是,如果没有分隔符,如何解决问题呢?我知道会有大量无意义的结果,但这不是我的观点。我只需要以最有效的方式获得所有可能的结果。

这将是一个输入:

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

这将是许多产出之一:

hello

你会怎么做?

3 个答案:

答案 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)。在实践中,这将贯穿与输入字符串匹配的可能单词的数量,因此如果只有几个选项,则此过程也将很快。

要注意,字符串越长,您就越有可能获得更多选项和更多可能的解释。

如果另外将可能的单词限制为预定义的集合,则可以修改第一遍以使用单词而不是单个字母。那么可能的解释数量应该减少很多,即使在较长的字符串上你的算法也会很快。