问题
I have an std::map
and I want to search for a key using a substring. For example, I have the following code:
#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;
int main(int argc, char *argv[])
{
TStrStrMap tMap;
tMap.insert(TStrStrPair("John", "AA"));
tMap.insert(TStrStrPair("Mary", "BBB"));
tMap.insert(TStrStrPair("Mother", "A"));
tMap.insert(TStrStrPair("Marlon", "C"));
return 0;
}
Now, I want to search for the position that holds the substring "Marl" and not "Marlon", if "Marla" is stored in the map. I want to find something that starts with "Marl". I need to find at most one position. Is this possible? If so, how?
I don't want to use any Boost libraries!
回答1:
You can't efficiently search for substring, but you can for prefix:
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
typedef map<string, string> TStrStrMap;
typedef pair<string, string> TStrStrPair;
TStrStrMap::const_iterator FindPrefix(const TStrStrMap& map, const string& search_for) {
TStrStrMap::const_iterator i = map.lower_bound(search_for);
if (i != map.end()) {
const string& key = i->first;
if (key.compare(0, search_for.size(), search_for) == 0) // Really a prefix?
return i;
}
return map.end();
}
void Test(const TStrStrMap& map, const string& search_for) {
cout << search_for;
auto i = FindPrefix(map, search_for);
if (i != map.end())
cout << '\t' << i->first << ", " << i->second;
cout << endl;
}
int main(int argc, char *argv[])
{
TStrStrMap tMap;
tMap.insert(TStrStrPair("John", "AA"));
tMap.insert(TStrStrPair("Mary", "BBB"));
tMap.insert(TStrStrPair("Mother", "A"));
tMap.insert(TStrStrPair("Marlon", "C"));
Test(tMap, "Marl");
Test(tMap, "Mo");
Test(tMap, "ther");
Test(tMap, "Mad");
Test(tMap, "Mom");
Test(tMap, "Perr");
Test(tMap, "Jo");
return 0;
}
This prints:
Marl Marlon, C
Mo Mother, A
ther
Mad
Mom
Perr
Jo John, AA
回答2:
When your substring is a prefix as in your example, you can use lower_bound to search for "Marl"
.
map<string,string>::const_iterator m = tMap.lower_bound("Marl");
cerr << (*m).second << endl;
This does not work for non-prefix substrings: in the general case, searching a map is not much different from searching other containers.
回答3:
To search for a substring of a key in a map you have no choice but to either use a new map on a special kind of key type or to search your map in O(n). std::map
uses (by default) operator<()
for ordering keys and for searching, and that compare function for std::string
is a plain lexicographical compare.
If you create a new map on a special key type that has operator<()
compare on basis of a substring take note that this will also affect the decision of whether a new element to insert would be a duplicate. In other words, such a map will only have elements that are not substrings of each other.
The O(n) search practically means you use std::find()
over the map, with a custom predicate that takes a std::pair<std::string,std::string>
and returns true if the second element of the pair is a substring of the first.
回答4:
typedef TStrStrMap::value_type map_value_type;
struct key_contains_substring
: std::binary_function<map_value_type, std::string, bool>
{
bool operator()(const map_value_type& map_value, const std::string& substr)
{
return std::search(map_value.first.begin(), map_value.first.end(),
substr.begin(), substr.end() != map_value.first.end());
}
};
...
TStrStrMap::const_iterator it = std::find_if(tMap.begin(), tMap.end(),
std::bind2nd(key_contains_substring(), "Marl");
回答5:
I'd like to expand on the answer by dasblinkenlight by providing a full solution using map::lower_bound(). As mentioned in the comments on that answer, you have to check whether lower_bound()
returns tMap.end()
. If not, then you also have to check whether the found key is actually prefixed with the search string. Latter can be checked, for example, by using string::compare(). As a result, my C++11 solution looks as follows:
std::map<std::string, std::string> myMap{
{"John", "AA"}, {"Mary", "BBB"}, {"Mother", "A"}, {"Marlon", "C"}, {"Marla", "D"}
};
std::string prefix("Marl");
auto it = myMap.lower_bound(prefix);
if (it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0)
std::cout << it->first << ": " << it->second << std::endl;
Output:
Marla: D
However, if you want to find all keys in your map that are prefixed with the search string, then you can use the following loop:
for (auto it = myMap.lower_bound(prefix); it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0; ++it)
std::cout << it->first << ": " << it->second << std::endl;
Output:
Marla: D
Marlon: C
Code on Ideone
来源:https://stackoverflow.com/questions/9349797/partial-match-for-the-key-of-a-stdmap