赞
踩
思路:
整体利用哈希表进行操作。
字母异位词的共同点就是,每个字母出现的次数一致,而字母之间内部是有序的,所以自然就可以相当,如果对两个字符串分别进行内部排序,排序之后得到的两个字符串如果一样,说明是字母异位词,反之则不是。
所以可以对字符串数组中的每个字符串进行排序,然后进行归类(如果排序后得到字符串相等,说明是一类,即是字母异位词),排序的复杂度为 N * log N,数据规模是104,可以接受,但是之后的查找如果用N2复杂度进行查找,则不能接受了。
所以可以使用哈希表,将排完序的字符串放入哈希表中,每排完一个字符串A需要去哈希表中查找是否有和该字符串属于字母异位词的单词B,如果有,则将A放入B所在数组位置,反之则额外放到一个新数组位置中,复杂度为O(N)
代码:
class Solution { public: vector<vector<string>> groupAnagrams(vector<string>& strs) { map<string,int>::iterator it; //哈希表第一维放字符串,第二维放对应答案数组中的位置 map<string,int> ma; vector<vector<string>> res; int n = strs.size(); int pivot = 0; for(int i=0;i<n;++i) { string temp = strs[i]; //对该字符串排序 sort(strs[i].begin(),strs[i].end()); //寻找有无字母异位词 it = ma.find(strs[i]); //有 if(it != ma.end()) { //放到数组同一位置 res[it->second].push_back(temp); } else { //放入新位置 res.push_back({temp}); //记录下来对应的数组下标 ma[strs[i]] = pivot; //数组下标向后移动 ++pivot; } } return res; } };
思路:
参考的题解思路,题解思路除了本思路外还有其他思路,在这里主要讲一种好理解、复杂度不高的思路——双指针。
由于数据规模是103,所以可以接受N2的复杂度的算法。
可以枚举每一个字符为回文子串的中间字符,双指针向两边界扩展,总的时间复杂度为O(n2),可以接受。
(最开始的思路是这样的,后来发现有情况没有考虑到——回文子串为偶数的情况,我开始尝试做了修补但是发现修补无用,所以还是老老实实参考了题解的思路)
这里采用了一个非常巧妙的思路来将偶数长度的回文子串也考虑进来!
对于回文子串,无论偶数长度还是奇数长度,区别在于中心字符,偶数长度的中心字符是两个一样的字符紧挨着,而奇数长度的中心字符则是一个。该方法的巧妙之处在于,在选定当前遍历的中心字符后,先进行了一次扩展,使得中心字符不一定是一个字符,可以是多个字符!
举个例子:s = “tattarrattat”
显然这是个回文串,其最长回文子串就是自身,而且是个偶数长度的回文子串。考虑当遍历到中心字符为第一个’r’时,此时会先判断其右指针的开端是在哪里(最开始右指针和左指针都指向中心字符),如果与中心字符紧挨着的下一个字符与中心字符相等,说明可能是偶数长度回文子串,所以将右指针位置放到下一个字符’r’上,然后继续判断是否再下一个字符’a’也与中心字符相等,如果相等则继续扩展,反之则停止,之后移动i指针,和将真正的左右指针进行赋值,向两边开始滑动判断。
那么问题又来了,可不可能会出现奇数回文子串被误判为偶数回文子串呢?
答案是不可能!
举个例子:s = “cccd”
为了方便叙述起见,称之为c1c2c3,模拟一遍代码:
首先i = 0,s[i] = c1,之后进行判断,右指针的下一个字符为c2,与c1都是字符’c’,所以可以扩展,右指针向后移动一位,继续判断。此时右指针下一个字符为c3,与c1都是字符’c’,继续扩展,之后再判断下一个字符为d,不是’c’,所以停止。此时得到的右指针位置在c3,左指针位置在c1,取到的字符串为ccc,是答案。之后下一次遍历,中心字符为d,得到的最长回文子串为d,比ccc要短,所以最后的答案为ccc,得到了正解!
代码:
class Solution { public: string longestPalindrome(string s) { //边界情况 if(s.size() <= 1) { return s; } int n = s.size(); string res; int i = 0; //枚举每一个字符 while(i < s.size()) { string now; int _left = i; int _right = i; //考虑偶数回文子串的情况 while(_right+1 < n && s[_left] == s[_right + 1]) { ++_right; } //继续增大中间字符的下标,向后遍历 i = _right + 1; int left = _left,right = _right; //双指针向两边界进行扩展,得到以当前字符为中间位置的最长回文子串 while(left-1 >= 0 && right+1 < n && s[left-1] == s[right+1]) { --left; ++right; } now = s.substr(left,right-left+1); //如果现在得到的子串更长,则替换 if(res.size() < now.size()) { res = now; } } return res; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。