如何在与用户给定前缀匹配的字符串向量中找到第一个单词?

Posted

技术标签:

【中文标题】如何在与用户给定前缀匹配的字符串向量中找到第一个单词?【英文标题】:How can I find the first word in a vector of strings that matches a user given prefix? 【发布时间】:2020-07-13 21:37:57 【问题描述】:

假设我有一个排序的字符串向量:

std::vector<std::string> Dictionary
Dictionary.push_back("ant");
Dictionary.push_back("anti-matter");
Dictionary.push_back("matter");
Dictionary.push_back("mate");
Dictionary.push_back("animate");
Dictionary.push_back("animal");
std::sort(Dictionary.begin(), Dictionary.end());

我想找到向量中与前缀匹配的第一个单词,但我找到的每个示例都使用硬编码字符串作为前缀。例如,我可以定义一个布尔一元函数来查找“an”前缀:

bool find_prefix(std::string &S) 
    return S.compare(0, 2, "an");

并将其用作std::find_if() 函数的谓词来查找第一个匹配项的迭代器。但是如何搜索用户给定的字符串作为前缀?是否可以以某种方式使用二元谓词?或者构建一个依赖于变量和参数的“伪一元”谓词?

或者,我应该在这个问题中使用任何其他容器和方法吗?

我知道有更高效和优雅的结构来存储用于前缀搜索的字典,但我是一个初学者自学编程,所以首先我想在冒险之前学习如何使用标准容器更复杂的结构。

【问题讨论】:

std::set 为您省去了排序的麻烦,并且您可以确保每个单词只出现一次。 C++20 将通过成员函数starts_with() 使这变得微不足道。在那之前,find()compare() 函数将让你到达那里。 另外,如果不是std::set,在向量上调用std::sort,然后使用std::lower_boundstd::upper_bound 将比线性搜索快得多。想象一下,如果有数千个字符串。 我第一次尝试将字典作为一个集合来实现,但我认为我不能在这个容器中使用 find_if()。 std::find_ifstd::set 配合得很好。 【参考方案1】:

您可以将find_prefix 写为 lambda。这使您可以捕获要搜索的字符串,并将其用于比较:

string word = ...  // the prefix you're looking for
auto result = std::find_if(Dictionary.begin(), Dictionary.end(), 
                           [&word](string const &S) 
                           return ! S.compare(0, word.length(), word);
);

【讨论】:

谢谢!我尝试使用 lambda 函数,但我无法超越第一个示例。所以我可以在括号之间使用任意数量的参数? 是的,您可以捕获多个变量。【参考方案2】:

由于您正在对向量进行排序,因此您应该利用向量已排序的优势。

您可以使用std::lower_bound 将您靠近(如果不正确)匹配前缀的条目,而不是进行线性搜索:

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

int main()

    std::vector<std::string> Dictionary;
    Dictionary.push_back("ant");
    Dictionary.push_back("anti-matter");
    Dictionary.push_back("matter");
    Dictionary.push_back("mate");
    Dictionary.push_back("animate");
    Dictionary.push_back("animal");
    std::sort(Dictionary.begin(), Dictionary.end());
    
    std::vector<std::string> search_test = "an", "b", "ma", "m", "x", "anti";
    for (auto& s : search_test)
    
        auto iter = std::lower_bound(Dictionary.begin(), Dictionary.end(), s);
    
        // see if the item returned actually is a match
        if ( iter->size() >= s.size() && iter->substr(0, s.size()) == s )
            std::cout << "The string \"" << s << "\" has a match on \"" << *iter << "\"\n";
        else
           std::cout << "no match for \"" << s << "\"\n";
    

输出:

The string "an" has a match on "animal"
no match for "b"
The string "ma" has a match on "mate"
The string "m" has a match on "mate"
no match for "x"
The string "anti" has a match on "anti-matter"

lower_bound 之后进行测试,看字符串是否与lower_bound 找到的字符串真正匹配。

【讨论】:

我知道lower_bound() 函数,但我没想过这样使用它。事后看来,由于使用字典顺序比较字符串,因此使用它来搜索前缀非常有意义。非常感谢! 您不需要如此复杂的检查,如果向量已排序,lower_bound 将返回正确的迭代器或 Dictionary.end()。所以你只需要比较迭代器 不,你必须检查。 lower_bound 返回排序向量中将插入项目的位置。您正在考虑std::binary_search,它返回truefalse。 See this example。 "b" 不匹配,但迭代器返回 "mate"

以上是关于如何在与用户给定前缀匹配的字符串向量中找到第一个单词?的主要内容,如果未能解决你的问题,请参考以下文章

从排序的字符串数组中找到第一个前缀匹配的最有效算法?

从排序字符串数组中找到第一个前缀匹配的最有效算法?

如何更改在与 Vuejs 中的 search_key 匹配的字符串中找到的子字符串的文本颜色?

使用std :: equal_range查找字符串向量中出现的前缀范围

如何实现最快的算法来匹配前缀与字符串?

如何在与 xpath 和 lxml 匹配的包含后找到 X 单元格的值