Given an array of strings, return all groups of strings that are anagrams.
Note: All inputs will be in lower-case.
For example:
Input: ["tea","and","ate","eat","den"]
Output: ["tea","ate","eat"]
Interface: vector<string>anagrams(vector<string>&strs);
A:
首先简单介绍一下Anagram(回文构词法)。Anagrams是指由颠倒字母顺序组成的单词,比如“dormitory”颠倒字母顺序会变成“dirty room”,“tea”会变成“eat”。
回文构词法有一个特点:单词里的字母的种类和数目没有改变,只是改变了字母的排列顺序。
由此我们可以想到,只要将几个单词按照字母顺序进行排序,就可以通过比较判断他们是否是anagrams。
思路:将字符串重新排序(sort),并把所有排序后的字符串存入multi_Map。之后利用equal_range()检索所有同key的字符串。之后存入到结果中。
背景知识:
1. equal_range():返回同key的一个迭代器的pair对象(element从first到last+1。该键关联的第一个实例,以及该键关联的最后一个实例的下一个位置)。如果找不到匹配的元素,pair对象中的两个迭代器都指向此键应该插入的位置。
equal_range()迭代: its.first 代表first元素, its.second 代表last+1元素。
auto its = map.equal_range('a'); for (auto it = its.first; it != its.second; ++it) { cout << it->first << '\t' << it->second << endl; }
2. map<k, v>::value_type: 返回一个pair类型,first元素对应const map<k,v>::key_type类型(键),second元素对应map<k,v>::mapped_type类型(值)(值可以修改,但是key不能修改)
map_it->first 取key; map_it->second 取value.
3. auto:
在C++11标准之前,auto关键字就被用来标识临时变量语义,在新的标准中,它的目的变成了另外两种用途。auto现在是一种类型占位符,它会告诉编译器,应该从初始化式中推断出变量的实际类型。当你想在不同的作用域中(例如,命名空间、函数内、for循环中中的初始化式)声明变量的时候,auto可以在这些场合使用。
auto i = 42; // i is an int auto l = 42LL; // l is an long long auto p = new foo(); // p is a foo*
使用auto经常意味着较少的代码量(除非你需要的类型是int这种只有一个单词的)。当你想要遍历STL容器中元素的时候,想一想你会怎么写迭代器代码,老式的方法是用很多typedef来做,而auto则会大大简化这个过程。
std::map<std::string, std::vector<int>> map; for(auto it = begin(map); it != end(map); ++it) { }
你应该注意到,auto并不能作为函数的返回类型,但是你能用auto去代替函数的返回类型,当然,在这种情况下,函数必须有返回值才可以。auto不会告诉编译器去推断返回值的实际类型,它会通知编译器在函数的末段去寻找返回值类型。在下面的那个例子中,函数返回值的构成是由T1类型和T2类型的值,经过+操作符之后决定的。
template <typename T1, typename T2> auto compose(T1 t1, T2 t2) -> decltype(t1 + t2) { return t1+t2; } auto v = compose(2, 3.14); // v's type is double
4. std::unordered_multimap::count
size_type count (const key_type& k) const;
key
的元素的数目.
class Solution { public: vector<string> anagrams(vector<string> &strs) { vector<string> res; if(strs.size() < 2) return res; unordered_multimap<string, int> collections; //put everything into multimap for(int i = 0; i < strs.size(); i++) { string s = strs[i]; //将字符串sort,如果是回文字符key值会一样。 sort(s.begin(), s.end()); collections.insert({s, i}); } //loop over unique key auto it = collections.begin(); while(it != collections.end()) { string s = it->first; //size_type count (const key_type& k) const; if(collections.count(s) > 1) { auto range = collections.equal_range(s); for(auto it = range.first; it != range.second; it++) { //按照对应的字符串索引添加到ret,it是迭代器,it->second代表map的key对应的value res.push_back(strs[it->second]); } } //让迭代器指向当前同key范围的下一个迭代器,即键大于k的第一个元素 it = collections.equal_range(s).second; } return res; } };
思路:用map<string, int>记录排序后的字符串以及首次出现的位置。
1. 从strs的第一个元素开始遍历,首先对元素进行排序得到s;
2. 在map里查找s;
3. 若不存在,将s以及该元素的下标存入map<string ,int>;
4. 若存在,首先将第一次出现s时的原始字符串存入结果res,即strs[map[s]],并将map[s]设置为-1(防止下次再存),再将该字符串本身存入结果res;
5. 重复以上1-4步,直到遍历结束。
AC Code:class Solution { public: vector<string> anagrams(vector<string> &strs) { vector<string> res; if(strs.size() < 2) return res; unordered_map<string, int> cache; for(int i = 0; i < strs.size(); i++) { string s = strs[i]; sort(s.begin(), s.end()); auto it = cache.find(s); if(it == cache.end()) { cache.insert({s, i}); } else { //存储当前回文字符串 res.push_back(strs[i]); if(it->second >= 0) { //第一次出现s时的原始字符串存入结果res,并且置为-1,防止重复写入 res.push_back(strs[it->second]); it->second = -1; } } } return res; } };本题其实降低了一点难度,就是输入的字符串全部为小写字母,这样就不需要处理大写字母,空格这些特殊情况了。