【问题标题】:Finding every possible word out of a bigger word [closed]从一个更大的词中找到每一个可能的词[关闭]
【发布时间】:2015-05-17 06:28:02
【问题描述】:

您好,我正在寻找一种算法来从 C++ 中的单个单词中提取每个可能的单词。
例如,从“溢出”这个词我可以得到这些:“爱”、“流”、“为”、“行”、“溢出”...
那么我怎样才能有效地只获得有效的英语单词。
注意:我有一本字典,一个很大的词表。

【问题讨论】:

  • 算法的某个特定部分是否存在问题,或者您只是希望有人为您完成全部工作?
  • 我只是不知道该怎么做,我想过找到所有可能的组合并检查它们,但是对于大于 5 个字符的单词会花费很多时间。
  • 我基本上是想做这个网站做的here
  • 如果你想要一种比你想的更快的算法,可能可以通过修改你想的算法来实现。

标签: algorithm


【解决方案1】:

如果不使用所有排列强制它,我想不出该怎么做。

类似这样的:

#include <string>
#include <algorithm>

int main()
{
    using size_type = std::string::size_type;

    std::string word = "overflow";

    // examine every permutation of the letters contained in word
    while(std::next_permutation(word.begin(), word.end()))
    {
        // examine each substring permutation
        for(size_type s = 0; s < word.size(); ++s)
        {
            std::string sub = word.substr(0, s);

            // look up sub in a dictionary here...
        }
    }

    return 0;
}

我可以想到 2 种方法来加快速度。

1) 检查给定排列的子字符串,以避免不必要的字典查找(std::setstd::unordered_set 可能)。

2) 缓存热门搜索结果,保留最常请求的单词(std::mapstd::unordered_map 可能)。

注意: 事实证明,即使在不同级别添加兑现后,对于较大的单词来说,这确实是一个非常慢的算法。

但是,这使用了更快的算法:

#include <set>
#include <string>
#include <cstring>
#include <fstream>
#include <iostream>
#include <algorithm>

#define con(m) std::cout << m << '\n'

std::string& lower(std::string& s)
{
    std::transform(s.begin(), s.end(), s.begin(), tolower);
    return s;
}

std::string& trim(std::string& s)
{
    static const char* t = " \t\n\r";
    s.erase(s.find_last_not_of(t) + 1);
    s.erase(0, s.find_first_not_of(t));
    return s;
}

void usage()
{
    con("usage: anagram [-p] -d <word-file> -w <word>");
    con("    -p             - (optional) find only perfect anagrams.");
    con("    -d <word-file> - (required) A file containing a list of possible words.");
    con("    -w <word>      - (required) The word to find anagrams of in the <word-file>.");
}

int main(int argc, char* argv[])
{
    std::string word;
    std::string wordfile;
    bool perfect_anagram = false;

    for(int i = 1; i < argc; ++i)
    {
        if(!strcmp(argv[i], "-p"))
            perfect_anagram = true;
        else if(!strcmp(argv[i], "-d"))
        {
            if(!(++i < argc))
            {
                usage();
                return 1;
            }
            wordfile = argv[i];
        }
        else if(!strcmp(argv[i], "-w"))
        {
            if(!(++i < argc))
            {
                usage();
                return 1;
            }
            word = argv[i];
        }
    }

    if(wordfile.empty() || word.empty())
    {
        usage();
        return 1;
    }

    std::ifstream ifs(wordfile);

    if(!ifs)
    {
        con("ERROR: opening dictionary: " << wordfile);
        return 1;
    }

    // for analyzing the relevant characters and their
    // relative abundance

    std::string sorted_word = lower(word);
    std::sort(sorted_word.begin(), sorted_word.end());

    std::string unique_word = sorted_word;
    unique_word.erase(std::unique(unique_word.begin(), unique_word.end()), unique_word.end());

    // This is where the successful words will go
    // using a set to ensure uniqueness
    std::set<std::string> found;

    // plow through the dictionary
    // (storing it in memory would increase performance)
    std::string line;
    while(std::getline(ifs, line))
    {
        // quick rejects

        if(trim(line).size() < 2)
            continue;

        if(perfect_anagram && line.size() != word.size())
            continue;

        if(line.size() > word.size())
            continue;

        // This may be needed if dictionary file contains
        // upper-case words you want to match against
        // such as acronyms and proper nouns
        // lower(line);

        // for analyzing the relevant characters and their
        // relative abundance

        std::string sorted_line = line;
        std::sort(sorted_line.begin(), sorted_line.end());

        std::string unique_line = sorted_line;
        unique_line.erase(std::unique(unique_line.begin(), unique_line.end()), unique_line.end());

        // closer rejects

        if(unique_line.find_first_not_of(unique_word) != std::string::npos)
            continue;

        if(perfect_anagram && sorted_word != sorted_line)
            continue;

        // final check if candidate line from the dictionary
        // contains only the letters (in the right quantity)
        // needed to be an anagram

        bool match = true;
        for(auto c: unique_line)
        {
            auto n1 = std::count(sorted_word.begin(), sorted_word.end(), c);
            auto n2 = std::count(sorted_line.begin(), sorted_line.end(), c);

            if(n1 < n2)
            {
                match = false;
                break;
            }
        }

        if(!match)
            continue;

        // we found a good one
        found.insert(std::move(line));
    }

    con("Found: " << found.size() << " word" << (found.size() == 1?"":"s"));
    for(auto&& word: found)
        con(word);
}

说明:

此算法通过专注于已知的良好模式(字典单词)而不是排列解决方案生成的大量不良模式来工作。

所以它会在字典中翻找与搜索词匹配的词。随着更明显的词被打折,它会根据准确度提高的测试连续打折。

使用的关键逻辑是搜索每个幸存的字典单词,以确保它包含搜索词中的每个字母。这是通过找到一个字符串来实现的,该字符串包含每个来自搜索词和字典单词的字母。它使用std::unique 来生成该字符串。如果它通过了这个测试,那么它会继续检查字典单词中每个字母的数量是否反映在搜索词中。这使用std::count()

仅当 所有 字母在字典单词和搜索词中匹配时,才会检测到 perfect_anagram。否则,搜索词包含至少足够的正确字母就足够了。

【讨论】:

  • 将第一个输入更改为“stackoverflow”,您的算法执行时间不合理。
  • 这与我正在做的类似,但它需要很长时间才能使用。我认为问题出在我的字典查找算法中。
  • @user2507237 你是对的,即使添加了缓存,这也很慢。但我被这个问题迷住了,并在这里写了一个更快的解决方案:ideone.com/JSKFQP
  • 嗯,这是一个快速算法,我刚刚尝试过,你能发布另一个答案并解释它吗(对不起,我真的只是一个初学者),如果不只是在这里发布它可能会有用给别人。谢谢,这正是我想要的。
  • @user2507237 不幸的是,该问题已作为 SO 题外话关闭,因此我无法发布另一个答案。但很高兴它对你有用。
猜你喜欢
  • 1970-01-01
  • 2014-10-16
  • 1970-01-01
  • 2011-07-23
  • 1970-01-01
  • 2010-10-27
  • 2021-06-07
  • 2021-06-09
  • 2020-01-23
相关资源
最近更新 更多