PHP中的字谜算法

Posted

技术标签:

【中文标题】PHP中的字谜算法【英文标题】:Anagram Algorithm in PHP 【发布时间】:2012-05-25 04:04:12 【问题描述】:

我完全是 php 新手。今天我遇到了一个我不知道如何解决的问题,即使在搜索谷歌和挖掘 SOF 之后。这是 Anagram 算法。

所以基本上,我理解这里的问题:当用户输入一个字符串时,我将它拆分并与我的库(给定数组)进行比较,然后我必须将它加入 2-3-...等字符以再对比一下,这正是我现在卡住的地方,我不知道如何加入数组的元素。

这是我正在实现的代码,也是一个示例字典。

我有一个自制的字典,其中包含数组 $dict 中的这些元素。我有一个表格供用户输入字符串,输入的字符串将传递给下面的代码并声明为 $anagram。我必须拆分输入的字符串以与我的字典进行比较。但我不知道如何加入它们,比如比较 2 个字母、3 个字母......等等......等等,与字典。

<?php

$dict = array(
'abde',
'des',
'klajsd',
'ksj',
'hat',
'good',
'book',
'puzzle',
'local',
'php',
'e');

$anagram = $_POST['anagram'];
//change to lowercase
$anagram = strtolower($anagram);

//split the string
$test = str_split($anagram);

//compare with $dict for the first split without joining
for ($i=0; $i<strlen($anagram); $i++) 
    if ($test[$i]==$dict[$i]) 
        echo $test[$i]."<br />";
    


//problem: how to join elements of the array in the loops
//like user inputs "hellodes"
//after echo "e", how to join the elements like: h-e,h-l,h-l,h-o,h-d,h-e,h-s
//and then h-e-l,h-e-l,h-e-o...etc...
?>

我希望算法尽可能简单,因为我完全是新手。我很抱歉,因为我的英语不太好。 最好的祝福, Khiem Nguyen。

【问题讨论】:

找到两个链接:sourceforge.net/projects/phpag 和 phpclasses.org/browse/file/12539.html 谢谢Gerep,我已经通读了它们,但这就像没用,因为它太复杂了,我无法理解。我希望有一个更简单的算法,只需使用循环连接字符串的元素并将其与库进行比较。 按字母顺序对字谜字符进行排序会不会更好,并且在循环中对每个字典单词执行相同的操作。如果字谜是字典单词的子串,那么它就是字谜 这是这个问题的单行答案***.com/a/32156857/4233593 【参考方案1】:

(我将此作为单独的答案添加,因为它处理问题的方式与我在第一期中提到的不同)

这是一种更复杂的方法,可以确定字典中的哪些单词是您要查找的单词的一部分;我将把它留给读者来弄清楚它是如何工作的。

它使用因式分解来确定一个词是否是另一个词的变位词。它将做的是为每个字母分配一个唯一的主要值;您可以通过将所有值相乘来计算给定单词中字母的值。例如,CAT 是 37 * 5 * 3 或 510。如果您的目标词因数相同,则可以确定一个是另一个的字谜。

我已经按照素数在英国英语中的常见程度对它们进行了排序,以保持生成的因子更小。

<?php

function factorise($word)

    // Take a number, split it into individual letters, and multiply those values together
    // So long as both words use the same value, you can amend the ordering of the factors 
    // as you like

    $factors = array("e" => 2, "t" => 3, "a" => 5, "o" => 7, "i" => 11,
        "n" => 13, "s" => 17, "h" => 19, "r" => 23, "d" => 29,
        "l" => 31, "c" => 37, "u" => 41, "m" => 43, "w" => 47,
        "f" => 53, "g" => 59, "y" => 61, "p" => 67, "b" => 71,
        "v" => 73, "k" => 79, "j" => 83, "x" => 89, "q" => 97,
        "z" => 101);

    $total = 1;

    $letters = str_split($word);

    foreach ($letters as $thisLetter) 
        if (isset($factors[$thisLetter])) 
            // This will skip any non-alphanumeric characters.
            $total *= $factors[$thisLetter];
        
    

    return $total;


$searchWord = "hasted";

$dict = array("abde", "des", "klajsd", "ksj", "hat", "hats");

$searchWordFactor = factorise($searchWord);

foreach ($dict as $thisWord) 
    // Factorise each word that we're looking for
    // If the word we've just factored is an exact divisor of the target word, then all the 
    // letters in that word are also present in the target word
    // If you want to do an exact anagram, then check that the two totals are equal

    $dictWordFactor = factorise($thisWord);

    if (($searchWordFactor % $dictWordFactor) == 0) 
        print ($thisWord . " is an anagram of " . $searchWord . "<br/>");
    

对于它的价值,我认为这是一个更优雅的解决方案 - 您可以通过预先计算字典中的值来加速它。如果你把字典中每个单词的因子都查一遍,你可以直接在数据库中进行搜索:

SELECT word FROM dictionary WHERE wordFactor='$factorOfThisWord'

【讨论】:

能否请您为上面的代码添加注释?我不知道函数因式分解是做什么的。 其实我是故意把cmets排除在外的;一段代码并没有那么复杂,所以你应该能够弄清楚它在做什么。尝试添加大量 var_dump 调用以查看正在设置的变量,然后从那里获取。 我们中的一些人并不打算实现这一点,但仍想了解它是如何工作的。为了我们,请发布 cmets... @andrewsi 我正在尝试长字 - 脚本失败的地方。例如。对于 $searchWord = "hastededrs";结果不显示。 :( @mujaffars - 这么短的东西不应该有任何问题。我只能建议您在代码中添加大量调试,并尝试找出它失败的原因。尝试将else 添加到if - 这会显示什么,还是您得到一个完全空白的屏幕?【参考方案2】:

我不太明白你的代码在做什么;但如果你想要一个简单的字谜检查器,伪代码将类似于:

get array of letters in my anagram
for each word in the dictionary
    get array of letters in this word
    for each letter in my anagram
        is this letter also in the word?
            if no, move on to the next word
    if we get here, it's an anagram

您可以做一些额外的事情 - 您可以确保字谜和字典单词的长度相同(如果不是,它们就不能是字谜);你还需要弄清楚如何处理字典单词中出现多次但在字谜词中只出现一次的字母(例如,上面的代码会将'aa'报告为'a'的字谜)

【讨论】:

很抱歉,我让你们陷入了麻烦之中。从一开始,就有一个表单供用户输入任意单词,这解释了为什么那里有一个 $_POST。 @andrewsi 我认为您的伪代码有问题,不是吗?因为您必须拆分用户输入的字符串,然后将它们连接起来进行比较,因为可能在 $dict 中只有 1 个字母,例如“a”、“e”等... 为什么要将字符串重新连接起来进行比较?上面的逻辑会将搜索词和字典词都拆分成数组,并比较每个数组的内容;字典单词是否是一个字母并不重要 - 你最终会得到一个只有一个项目的数组。 因此我必须拆分:例如,我上面的字典包含“hat”和“e”,而用户输入的字符串是“hatedes”。主要目标是打印出与 dict 匹配的字谜,所以这次它会打印出 'hat' 'e' 和 'des' 因为 dict 包含它。如果比较每个数组的内容,如果用户输入的数组比字典的数组长怎么办? 数组的长度无关紧要;您只需要检查以确保其中一个的内容与另一个的内容匹配。因此,如果字典中的单词是“a”,而输入的单词是“土豚”,那么您会匹配,因为 a 在那里。单词中的其他字母是什么并不重要。 我没有说任何关于排序的事情——你的意思是为了别人吗?【参考方案3】:

我无法理解您的问题、您对代码的解释以及代码本身。您要检查任意单词是否是字典中某个单词的字谜?

这很简单——创建一个包含 26 个整数的数组。遍历小写的输入单词,每个字母将 array[letter - 'a'] (或任何 php 等效项)增加 1。

然后遍历字典并为每个单词以相同的方式生成 array_dict,并检查 i = 0...25 if array[i] == array_dict[i]。如果它们都相同,则这些词是字谜。当然,在每个单词之后将 array_dict 设置为零。

另一种方法是对字符串中的字母进行排序,然后简单地比较排序后的字符串。如果允许您修改/预处理字典,这很好 - 您保持字典预先排序,然后只需对输入单词进行排序并将其与字典单词进行比较。最佳解决方案可能是创建一个(用 C# 术语,我不知道 php 抱歉)

Dictionary<string, List<string>>

并通过对每个单词进行排序,在字典中查找它来预处理您的字典,如果列表不存在,则创建它,并在任何一种情况下将单词添加到列表中。然后,当用户输入单词时,您可以对其进行排序并返回 dictionary[sortedword] 作为结果 - 在基本恒定的时间内找到所有字谜(输入字符串长度为 nlogn,但字典大小为恒定)。

【讨论】:

【参考方案4】:
$dictionary = array("kayak");

$anagram = "kayak";

$anagramSorted = sortString($anagram);


foreach ($dictionary as $word)

    $wordSorted = sortString($word);
    if ($wordSorted == $anagramSorted)
    
       echo 'true';
    


function sortString($s)

    $chars = array();
    $length = strlen($s);
    for ($i=0;$i<$length;$i++)
    
       $chars[] = $s[$i];
    
    sort($chars);

    return implode("",$chars);

【讨论】:

感谢 gunnx,但我有这方面的疑惑。比如我的字典有'hat'这个词,然后你排序,它变成'aht',用户输入的字符串是'ath'。因此,如果您对它们进行排序,它们就会匹配!但是看看,用户输入的单词与dict(ath和hat)不匹配。 你也对输入的词进行排序,如代码$anagramSorted所示 如果你同时对输入的字符串和字典中的单词进行排序,它完全改变了!就像我上面的例子一样,我可以给你更多:字典有“好”,用户输入“doog”,如果你对两者都进行排序,它们完全匹配。但是输入的字符串不匹配,并且不在字典中。 抱歉,不确定您的意思,但我看到您已经接受了答案,所以希望它对您有用。【参考方案5】:

试试字符串洗牌功能?

str_shuffle ( string $str )

这是一些伪代码:

Get random string from array
store string copy (Not shuffled)
string shuffle another copy
echo shuffled string
get users guess
parse guess (Remove illegal characters)
if parsed guess = string
    reward
else
    ?let user try again?

【讨论】:

【参考方案6】:

此函数将获取字符串并返回字符串中存在的计数字谜。

function countingAnagrams($str)
    
        $str_arry = [];
        $anagrams = 0;
        $str_arry= explode(' ', $str);
        for ($i = 0; $i < count($str_arry); $i++) 
            $str_cmp = $str_arry[$i];
            for($k = 0; $k < count($str_arry); $k++)
                if($i != $k)
                    $str_rev = $str_arry[$k];
                    if (count_chars($str_cmp, 1) == count_chars($str_rev, 1))
                    
                        unset($str_arry[$i]);
                        $str_arry = array_values($str_arry);
                        $anagrams++;
                    
                
            
        
        return $anagrams;
    


echo  countingAnagrams('cars are residing on my arcs');

【讨论】:

以上是关于PHP中的字谜算法的主要内容,如果未能解决你的问题,请参考以下文章

此字谜子串搜索算法中的数学

php |字谜求解器通过 woldcard 查找不在原始搜索中的字母

字谜字符串编辑距离算法/代码?

php - 向字谜求解器添加通配符

文件中的所有字谜

PHP 字谜解谜器