zoukankan      html  css  js  c++  java
  • 【LeetCode】Anagram

    Anagram 指由颠倒字母顺序而构成的单词。

    e.g. 给出 ["eat", "tea", "tan", "ate", "nat", "bat"],假设所给单词仅由小写字母组成。

    返回

    [
        ["ate", "eat","tea"],
        ["nat","tan"],
        ["bat"]
    ]

    我的算法原理是 key - value,但不会使用 map,所以效率极低。

     1 vector<vector<string>> groupAnagrams(vector<string>& strs) {
     2     vector<vector<string>> result;
     3     vector<string> key;
     4     for (string str : strs) {
     5         string s = str;
     6         sort(s.begin(), s.end());
     7         vector<string>::iterator it = find(key.begin(), key.end(), s);
     8         if (it == key.end()) {
     9             key.push_back(s);
    10             result.push_back({str});
    11         } else {
    12             result[it - key.begin()].push_back(str);
    13         }
    14     }
    15     return result;
    16 }

    看到别人一种算法原理和我完全一样,但使用了 unordered_map 和 multiset,效率大大提高。

    unordered_map 不会根据 key 的大小进行排序,存储时是根据 key 的 hash 值判断元素是否相同。因此如果存放自定义类型,那么就需要重载 operator== 以及 hash_value 函数。

    multiset 是 <set> 库中一个非常有用的类型。set 的特性是所有元素都会根据值自动排序;set 不允许两个元素拥有相同的值;不能通过迭代器修改 set 元素的值。

    set 和 multiset 都是基于红黑树实现的,因此元素在插入 / 删除的过程中就实现了排序,不同的是后者允许元素重复而前者不允许。

     1 vector<vector<string>> groupAnagrams(vector<string>& strs) {
     2     unordered_map<string, multiset<string>> mp;
     3     for (string s : strs) {
     4         string t = s; 
     5         sort(t.begin(), t.end());
     6         mp[t].insert(s);
     7     }
     8     vector<vector<string>> anagrams;
     9     for (auto m : mp) { 
    10         vector<string> anagram(m.second.begin(), m.second.end());
    11         anagrams.push_back(anagram);
    12     }
    13     return anagrams;
    14 }

    由于条件 “所给单词仅由小写字母组成”,将 O(nlogn) 的 sort 换成基于 counting sort (O(n)时间)的排序函数,也会提高一定效率。

     1 vector<vector<string>> groupAnagrams(vector<string>& strs) {
     2     ...
     3     string t = strSort(s);
     4     ...
     5 }
     6 
     7 string strSort(string& s) {
     8     int count[26] = {0}, n = s.length();
     9     for (int i = 0; i < n; i++)
    10         count[s[i] - 'a']++;
    11     int p = 0;
    12     string t(n, 'a');
    13     for (int j = 0; j < 26; j++)
    14         for (int i = 0; i < count[j]; i++)
    15             t[p++] += j;
    16     return t;
    17 } 

        计数排序是一个非基于比较的排序算法,它的优势在于在对一定范围内的整数(即输入的线性表元素属于有限偏序集)排序时,它的复杂度为 Ο(n+k)(其中 k 是整数的范围),快于任何比较排序算法,但当 O(k) > O(nlogn) 时其效率不如比较排序。这是一种牺牲空间换取时间的做法。

    算法思想:扫描整个序列,对每一个元素 x,确定该序列中值小于等于 x 的元素的个数。如果输入序列中只有 17 个元素的值小于等于 x 的值,则 x 可以直接存放在输出序列的第 18 个位置上。如果有重复元素时,我们不能将这些元素放在输出序列的同一个位置上,因此还要作适当的修改。

  • 相关阅读:
    UVA
    UVA
    模板——扩展欧几里得算法(求ax+by=gcd的解)
    UVA
    模板——2.2 素数筛选和合数分解
    模板——素数筛选
    Educational Codeforces Round 46 (Rated for Div. 2)
    Educational Codeforces Round 46 (Rated for Div. 2) E. We Need More Bosses
    Educational Codeforces Round 46 (Rated for Div. 2) D. Yet Another Problem On a Subsequence
    Educational Codeforces Round 46 (Rated for Div. 2) C. Covered Points Count
  • 原文地址:https://www.cnblogs.com/wayne793377164/p/7233094.html
Copyright © 2011-2022 走看看