用递归函数解码anagram并不能给出预期的输出

时间:2016-06-05 20:00:48

标签: php function recursion anagram

所以我试图将字谜解码为我的字典文件中的单词。但是我的递归函数并不像我期待的那样表现。

关于代码的想法是消除在单词上使用的字母,并输出它出现的字符串。

<?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个字来自单词列表。

有人可以在此处加倍吗?

1 个答案:

答案 0 :(得分:3)

情况

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

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

  

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

您的代码

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

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


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


解决方案

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

理论

首先,我们有一个字谜和字典。现在我们首先用字谜过滤字典,只保留可以用字谜构建的单词。

然后我们浏览所有单词,对于每个单词,我们将其添加到可能的解决方案中,将其从anagram中删除,通过新的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()的帮助下进行检查,可以用给定的字谜构建单词列表中的哪些单词。然后它返回单词列表中所有可能的单词,可以使用anagram构建。

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

    return $tmp;

}

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

在函数本身中,我们基本上只是遍历单词,对于每个单词,我们将它从anagram中删除,通过新的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
        )

)

(忽略这里的密钥,因为这些是解决方案的标识符)