码迷,mamicode.com
首页 > 其他好文 > 详细

[LeetCode] 在一堆字符串中找出包含相同字符的 group的较快方法,题 Anagrams

时间:2014-05-04 20:48:08      阅读:608      评论:0      收藏:0      [点我收藏+]

标签:style   blog   class   code   java   color   

题目:

Given an array of strings, return all groups of strings that are anagrams.

Note: All inputs will be in lower-case.

bubuko.com,布布扣
class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
    }
};
bubuko.com,布布扣

 

题意本身并不是很清晰,开始我的代码总是报Output Limit Exceeded,后来搜了相关文章,明白了题目真正要求的输出格式。

For example:

Input:  ["tea","and","ate","eat","den"]

Output:   ["tea","ate","eat"]

 

开始,我的思路是,将每一个string 都和其他比较,互为anagram的就记录到vector<string> res中。最后返回res。

这样宏观上来需要O(n2)次,n是输入vector的元素个数;对于内部判断anagram,我自己用数组实现dictionary[26],记录每一个character出现的次数,两个string如果正好可以让dictionary的全部元素回归0,则互为anagram,这样内部判断的时间是O(m),m是string的长度。

写这段代码时,我对输出的理解还存在错误,以为对于所有anagram group,只要将这个group中的第一个放入返回的vector<string>中即可。所以下面代码中,如果res中后面的元素已经判定和res中靠前的string互为anagram,后面的元素会被从res中移除。

初次实现的代码如下:

bubuko.com,布布扣
class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
        vector<string> res;
        if(strs.size() == 0) return res;
        dic = new int[26];
        for(vector<string>::iterator it = strs.begin(); it < strs.end(); ++it){
            res.push_back(*it);
        }
        for(int i = 0; i < res.size(); ++i){
            for(int j = i+1; j < res.size(); ++j){
                initDic(dic, 26, res[i]);
                int k = 0;
                for(; k < res[j].length(); ++k){ //判断 res[i] 和res[j] 是否为anagrams
                    dic[res[j][k] - a]--;
                    if(dic[res[j][k] - a] < 0) break;
                }
                if(k == res[j].length() && judgeDic(dic, 26)){
                    res.erase(res.begin() + j); //移除和res中的元素互为anagram的
                    --j;
                }
            }
        }
        return res;
    }
private:
    int* dic;
    void initDic(int* dic, int n, string str){
        for(int i = 0; i < n; ++i){
            dic[i] = 0;
        }
        for(int j = 0; j < str.length(); ++j){
            dic[str[j] - a]++;
        }
    }
    bool judgeDic(int* dic, int n){
        int i = 0;
        for(; i < n; ++i){
            if(dic[i] != 0) break;
        }
        return (i == n);
    }
};
bubuko.com,布布扣

这样做,超时。

原因就在于宏观上的O(n2),应该有优化的余地。Annie Kim‘s Blog中介绍了空间换时间的做法,即定义一个map<string, int>,然后遍历strs的元素,对于strs中的每一个string s,先将s的内容排序,再将排好序的s当作key。

这样虽然排序本身需要O(mlogm)的时间(m是string的长度),但是宏观上,只需要O(n)的时间(n是输入vector的元素个数),因为map的访问是O(1)。

因此整体上时间复杂度可能会下降(测试用例的n较大时)。

但是这个思路的缺点在于:因为是将string 排序后本身作为key,因此如果题目增加难度,比如string中包含标点和空格,那么这种方法就不能准确判断两个string是否anagram了。另外,如果string非常长,用来做key也不是很方便。

我结合我自己的思路做了一些修改,修改后的思路中,key不是排完序的string,而是依然利用我开始代码里面的dic[26]:先从头到尾扫一遍string,然后给dic对应位置+1,然后将dic元素本身的排列作为key。这样,(1) 在有空格和标点的情况下,依然可以判断两个string是否是anagram,如果有大写字母或者数字,只需要扩张dic的大小即可;而且Key的长度为定值,这里总是26。(2) 不再需要O(mlogm)的时间复杂度,需要O(m+26) = O(m)的复杂度。

实现代码如下:

bubuko.com,布布扣
class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
        vector<string> res;
        if(strs.size() == 0) return res;
        map<string, int> rec; 
        dic = new int[26];
        for(int i = 0; i < strs.size(); ++i){
            string key = generateKeyByDic(dic, 26, strs[i]);
            if(rec.find(key) == rec.end()){
                rec.insert(make_pair(key, i));
            }else{
                if(rec[key] >= 0){
                    res.push_back(strs[rec[key]]);
                    rec[key] = -1;
                }
                res.push_back(strs[i]);
            }
        }
        return res;
    }
private:
    int* dic;
    string generateKeyByDic(int* dic, int n, string str){
        for(int i = 0; i < n; ++i){
            dic[i] = 0;
        }
        for(int j = 0; j < str.length(); ++j){
            if(str[j] <= z && str[j] >= a)
                dic[str[j] - a]++;
        }
        string key(26, 0);
        for(int k = 0; k < 26; ++k){
            key[k] = dic[k] + 0;
        }
        return key;
    }
};
bubuko.com,布布扣

100 / 100 test cases passed. Runtime: 224 ms

而是用sorted string做key的方法,数据是 100 / 100 test cases passed. Runtime: 228 ms

时间上并没有提高多少,原因应该是test case的string长度都不算大,故O(mlogm)和O(m+26) 差别不大。

 

总结:

不论是引用的思路,还是我的思路,核心都是使用了map<string, int>,当需要在一堆字符串中找出包含相同字符的 group,这种空间换时间的方法可以考虑。

[LeetCode] 在一堆字符串中找出包含相同字符的 group的较快方法,题 Anagrams,布布扣,bubuko.com

[LeetCode] 在一堆字符串中找出包含相同字符的 group的较快方法,题 Anagrams

标签:style   blog   class   code   java   color   

原文地址:http://www.cnblogs.com/felixfang/p/3706217.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!