减少字符串比较的时间复杂度

时间:2015-07-23 20:29:10

标签: c++ string

我有一个dictionary .txt文件,可能有超过一千个单词及其定义。我已经编写了一个程序来从该文件中获取每行的第一个单词,并根据用户输入的字符串进行检查:

void checkWord(string input)
{

    std::ifstream inFile;
    inFile.open("Oxford.txt");
    if (inFile.is_open())
    {
        string line; //there is a "using std::string" in another file
        while (getline(inFile, line))
        {
            //read the first word from each line
            std::istringstream iss(line);
            string word;
            iss >> word;
            //make sure the strings being compared are the same case
            std::transform(word.begin(), word.end(), word.begin(), ::tolower);
            std::transform(input.begin(), input.end(), input.begin(), ::tolower);
            if (word == input)
            {
                //Do a thing with word
            }
        }
        inFile.close();
        return "End of file";
    }
    else
    {
        return "Unable to open file";
    }

}

但如果我检查的不仅仅是一个句子,那么处理所需的时间就会变得明显。我已经考虑过将这个时间缩短的几种方法:

  • 为字母表中的每个字母制作一个.txt文件(很容易做到,但从长远来看并不是真正的修复)
  • 使用unordered_set比较字符串(如this问题),唯一的问题可能是从文本文件中初步创建这些地图
  • 使用其他一些数据结构来比较字符串? (如std :: map)

鉴于数据已经排序"我应该采用什么样的数据结构或方法以便(如果可能的话)减少时间复杂度?另外,我用来比较字符串的函数有什么问题吗? (例如,string :: compare()会比" =="更快?)

4 个答案:

答案 0 :(得分:6)

树(std::unsorted_map)?还是一个hashmap(break)?你的线性搜索显然是一个强力解决方案!对于多次搜索,上述两者都基本优越。

当然,如果您要在每个程序运行中多次使用此数据,这只会在您的问题中未指定,这才真正有用。如果没有,那么加载和解析以及存储所有数据只是为了执行单个查找然后退出并没有太大的好处。只需将library(ggplot2) summ$Lambda <- log10(summ$Lambda) ggplot(summ, aes(x=factor(Lambda), y=means)) + geom_boxplot() + geom_line(inheret.aes = FALSE, aes(x=factor(Lambda), y=means, group = 1), color = "blue") + ylab("Mean") + xlab("Lambda (Log10)") + scale_x_discrete(labels = round(summ$Lambda,2)) + theme( axis.ticks.y = element_blank() , axis.text.x = element_text(angle = 45, hjust = 1) ) 置于成功之中,至少。

您暗示您的输入文件已排序。您可以将文件搜索(这非常便宜)的二进制搜索解决方案混合在一起,并在每次迭代时捕捉到最近的换行符,以大致确定所有具有相同前导(例如)三个字符的单词在您的文件中的位置。但是,对于一千个参赛作品来说,这可能有点过头了。

答案 1 :(得分:1)

不是将所有内容存储在.txt文件中,而是将其存储在真实的数据库中。

SQLite3是简单任务的不错选择,因为它在进程中而不需要外部服务器。

非常简单,C API和SQL语句应该很容易学习。

类似的东西:

-- Only do this once, for setup, not each time you run your program.
sqlite> CREATE TABLE dictionary (word TEXT PRIMARY KEY);
sqlite> .import /usr/share/dict/words dictionary;
-- Do this every time you run your program.
sqlite> select count(*) from dictionary where word = 'a';
1

答案 2 :(得分:1)

所以,有简单的&#34;修复,还有一些更复杂的。

第一步是将所有不必要的东西从搜索循环中移出:小写input一次,在循环之前,而不是每次 - 毕竟,它不会改变。如果可能,也会将Oxford.txt小写,因此您不必为每一行小写word

如果您多次搜索文件,多次读取文件绝对不是一个好方法 - 即使它第二次在文件系统中缓存。

所以将它读入一个容器,非常简单的就是std::vector [并且同时将字符串小写]并且只是迭代它。下一个改进是对矢量和我们进行二分搜索(但你必须自己编写二进制搜索 - 它并不是非常难)

稍微复杂的解决方案[但搜索速度更快]将使用std::map<std::string, std::string> wordlist(但也需要更多空间),然后使用auto pos = wordlist.find(input); if (pos != wordlist.end() ... found word ...

答案 3 :(得分:1)

您可以使用前缀树,也称为 trie 数据结构,因为它适合拥有字典且频繁使用的用例在里面查找单词。

trie的最简单模型是一个树,其中每个节点都有一个字母和一个标志,用于判断当前字母是否是单词的结尾(另外,还有指向该单词的其他数据的指针)。

包含字典aback abate bid bird birth black blast的trie的示例图片:

Trie programmer art

要搜索单词,请从根目录开始,对于单词的每个字母,请按照包含当前字母的节点进行操作(如果它不是当前节点的子节点,则暂停)。搜索时间与查找字长成比例,而不是字典的大小。

trie 还允许您轻松地在字典中获得字母的字母(词典编纂)顺序:只需执行 pre-order traversal