使用递归函数解码字谜不会给出预期的输出

Posted

技术标签:

【中文标题】使用递归函数解码字谜不会给出预期的输出【英文标题】:Decoding anagram with recursive function doesn't give expected output 【发布时间】:2016-10-05 09:34:54 【问题描述】:

所以我正在尝试将一个字谜解码为我的字典文件中的单词。但我的递归函数并没有像我预期的那样表现。

关于代码的想法是消除用于单词的字母并将其输出的字符串输出给我。

<?php

function anagram($string, $wordlist)

    if(empty($string))
        return;

    foreach($wordlist as $line)
    
        $line = $org = trim($line);
        $line = str_split($line);
        sort($line);

        foreach($line as $key => $value)
        
            if($value != $string[$key])
            
                continue 2;
            
        

    echo $org . anagram(array_slice($string, count($line)), $wordlist); 
    

    echo PHP_EOL;




$string = "iamaweakishspeller";
$string = str_split($string);
sort($string);

$file = file('wordlist');

anagram($string, $file);

这是我现在的结果,看起来很糟糕,但我的代码有一些问题 - 它进入了一个无限循环,单词列表中大约有 200 个单词。

有人可以在这方面采取额外的高峰吗?

【问题讨论】:

我不太明白你想对函数内部的调用做什么:anagram(array_slice($string, count($line)), $wordlist)?我的意思是你只是想看看你的字谜是否在你的单词文件中,对吧? @Rizier123,不,字谜可以是多个单词组合成一个句子 @Repox 所以你的文本可以包含保存在你的文件中的多个变位词,你想找到所有的吗? @Rizier123,正确。 那么这个问题我们在哪里? 【参考方案1】:

情况

你有一个字典(文件)和一个包含一个或多个单词的字谜。字谜不包含原始单词的任何标点符号或字母大小写。

现在您想找到所有真正的解决方案,在这些解决方案中,您用完字谜的所有字符并将其解码为字典中的单词。

注意:您可能会找到多种解决方案,但您永远不会知道原始文本是哪一个以及单词的顺序,因为多个单词的字符混合在字谜并且您没有标点符号或其中的字母大小写。

您的代码

您当前代码中的问题正是您将多个单词混合在一起。如果您现在对它们进行排序,并且想在字典中搜索它们,您将无法找到它们,因为多个单词的字符混合在一起。示例:

anagram  = "oatdgc"  //"cat" + "dog"
wordList = ["cat", "dog"] 


wordListSorted    = ["act", "dgo"]
anagramSorted     = acdgot
                    ↓↓↓
WordListSorted[0] → cat   ✗ no match
WordListSorted[1] → dog   ✗ no match

解决方案

首先我将在理论上解释我们如何构建所有可能的真实解决方案,然后我会解释代码中的每个部分是如何工作的。

理论

首先我们有一个字谜和字典。现在我们首先通过字谜过滤字典,只保留字词,可以由字谜构造。

然后我们遍历所有单词,并将每个单词添加到一个可能的解决方案中,将其从 anagram 中删除,通过新 anagram 过滤字典并递归调用具有新值的函数。

我们这样做直到字谜为空并且我们找到了一个真正的解决方案,我们将其添加到我们的解决方案集合中,或者没有剩余的单词并且它不是一个可能的解决方案。

代码

我们的代码中有两个辅助函数 array_diff_once()preSelectWords()

array_diff_once() 与内置的array_diff() 函数几乎相同,只是它只删除一次而不是所有出现的值。否则没有太多可解释的。它只是遍历第二个数组,并在第一个数组中删除一次值,然后返回。

function array_diff_once($arrayOne, $arrayTwo)
    foreach($arrayTwo as $v) 
        if(($key = array_search($v, $arrayOne)) !== FALSE)
            array_splice($arrayOne, $key, 1);
    

    return $arrayOne;


preSelectWords() 将一个字谜和一个单词列表作为参数。它只是在array_diff_once() 的帮助下检查,可以使用给定的字谜构造单词列表中的哪些单词。然后它从单词列表中返回所有可能的单词,可以用字谜构造。

function preSelectWords($anagram, $wordList)
    $tmp = [];
    foreach($wordList as $word)
        if(!array_diff_once(str_split(strtolower($word)), $anagram))
            $tmp[] = $word;
    

    return $tmp;


现在到主函数decodeAnagram()。我们将首先使用preSelectWords() 过滤的字谜和单词列表作为参数传递给函数。

在函数本身中,我们基本上只是遍历单词,对于每个单词,我们将其从 anagram 中删除,通过新的 anagram 过滤单词列表并将单词添加到可能的解决方案中并递归调用函数。

我们这样做直到字谜为空并且我们找到了一个真正的解决方案,我们将其添加到我们的解决方案数组中,或者列表中没有留下任何单词并且没有可能的解决方案。

function decodeAnagram($anagram, $wordList, $solution, &$solutions = [])

    if(empty($anagram) && sort($solution) && !isset($solutions[$key = implode($solution)]))
        $solutions[$key] = $solution;
        return;
    

    foreach($wordList as $word)
        decodeAnagram(array_diff_once($anagram, str_split(strtolower($word))), preSelectWords(array_diff_once($anagram, str_split(strtolower($word))), $wordList), array_merge($solution, [$word]), $solutions);


代码

<?php

    function decodeAnagram($anagram, $wordList, $solution, &$solutions = [])

        if(empty($anagram) && sort($solution) && !isset($solutions[$key = implode($solution)]))
            $solutions[$key] = $solution;
            return;
        

        foreach($wordList as $word)
            decodeAnagram(array_diff_once($anagram, str_split(strtolower($word))), preSelectWords(array_diff_once($anagram, str_split(strtolower($word))), $wordList), array_merge($solution, [$word]), $solutions);

    

    function preSelectWords($anagram, $wordList)
        $tmp = [];
        foreach($wordList as $word)
            if(!array_diff_once(str_split(strtolower($word)), $anagram))
                $tmp[] = $word;
        

        return $tmp;

    

    function array_diff_once($arrayOne, $arrayTwo)
        foreach($arrayTwo as $v) 
            if(($key = array_search($v, $arrayOne)) !== FALSE)
                array_splice($arrayOne, $key, 1);
        

        return $arrayOne;

    



    $solutions = [];
    $anagram = "aaaeeehiikllmprssw";
    $wordList = ["I", "am", "a", "weakish", "speller", "William", "Shakespeare", "other", "words", "as", "well"];
             //↑ file("wordlist", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)

    decodeAnagram(str_split(strtolower($anagram)), preSelectWords(str_split(strtolower($anagram)), $wordList), [], $solutions);
    print_r($solutions);


?>

输出

Array
(
    [Iaamspellerweakish] => Array
        (
            [0] => I
            [1] => a
            [2] => am
            [3] => speller
            [4] => weakish
        )

    [ShakespeareWilliam] => Array
        (
            [0] => Shakespeare
            [1] => William
        )

)

(忽略这里的键,因为它们是解决方案的标识符)

【讨论】:

感谢您的努力,但这不起作用。我只得到排序的字谜字符串。 好吧,我只是把你制作的确切代码放入正确的 word 文件和字谜,我得到的确切结果是 Original: a Anagram: aaaeeehiikllmprssw - 没有别的。我知道那个字谜的答案是Willam Shakespeare 并且两个“单词”都存在于单词本中,我得到了前面提到的结果。 @Repox 好吧,如果您已经在字谜中将两个单词混合在一起并删除了标点符号,那将会更加复杂,因为您现在可以有多个匹配项,因为您将所有内容混合在一起。 那是实际的问题。标点符号和空格不是字谜的一部分。带有空格的原始字谜是I am a weakish speller,然后可以转换为William Shakespeare @Repox 只是为了确保并说清楚:字谜中没有标点符号,并且忽略了原始单词的大小写,对吗?

以上是关于使用递归函数解码字谜不会给出预期的输出的主要内容,如果未能解决你的问题,请参考以下文章

Python - 递归字谜函数

使用arguments属性的递归Javascript函数给出了正确的答案,但返回undefined

如何在不递归的情况下找到所有可能的字谜?

使用python中的递归解决方案在字符串列表中查找字谜

字谜递归Scala

python第三天学习复习,集合set,文件操作,函数(普通函数,递归,高阶函数),字符编码和解码