242. 有效的字母异位词
题目介绍:
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
提示:
1 <= s.length, t.length <= 5 * 104
s 和 t 仅包含小写字母
思路:
哈希表主要用于快速查找某一元素是否存在,本题因为都是小写字母,故可以创建一个26位的数组record,随后先将字符串s包含的字母映射至数组(将s[i] - ‘a’ 即可),每个字母出现一次便加一,
再遍历字符串t,每个字母出现一次便减一。若最后数组内的元素都为0,则说明是字母异位词。
具体代码如下:
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26] = {0} ; //定义数组,记录字母出现的次数
for( int i = 0 ; i < s.size() ; i++ ){
//与‘a'相减得到ASCII码作为该字母的索引
record[ s[i] - 'a' ]++ ;
}
for( int i = 0 ; i < t.size() ; i++ ){
record[ t[i] - 'a' ]-- ;
}
for(int i = 0 ; i <26 ; i++){
//如果存在不为0的元素,说明一定存在一个字符串多了或者少了字符
if( record[i] != 0 ) return false ;
}
return true ;
}
};
383. 赎金信
题目介绍:
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
示例 1:
输入:ransomNote = "a", magazine = "b"
输出:false
示例 2:
输入:ransomNote = "aa", magazine = "ab"
输出:false
示例 3:
输入:ransomNote = "aa", magazine = "aab"
输出:true
提示:
1 <= ransomNote.length, magazine.length <= 105
ransomNote 和 magazine 由小写英文字母组成
思路:
1、判断ransomNote 的长度是否大于magazine的长度
2、创建record数组,先记录magazine的所有字母及数量,字母每出现一次,数量加一
3、遍历ransomNote ,字母每出现一次数量减一
4、遍历数组是否存在小于0的元素,若小于0则表示magazine中缺少此字母
具体代码如下:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
//如果 ransomNote 的长度大于magazine的长度直接返回false
if(ransomNote.size() > magazine.size() ) return false ;
int record[26] = {0} ;
//记录magazine的所有字母及数量
for( char mag : magazine ){
++record[ mag - 'a' ] ;
}
for( char ran : ransomNote){
--record[ ran - 'a' ] ;
}
//若有元素小于0,则代表magazine中缺少此字母
for( int i =0 ; i < 26 ; i++ ){
if( record[i] < 0 ) return false ;
}
return true ;
}
};
49. 字母异位词分组
题目介绍:
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
提示:
1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] 仅包含小写字母
思路:
1、先创建mapStr用于记录字母及数量,其中key形式为字母+数量,如:a1b2f3,value为key对应的异位词集合
2、遍历strs,创建数组record记录下每个元素的字母及数量
3、遍历数组拼接出key,并存入mapStr中
4、遍历map,将所有map的value值传入result中
具体代码如下:
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map< string , vector<string> > mapStr ;
vector<vector<string> > result;
for( string str : strs )
{
int record[26] = {0} ;
//记录下str的字符,及其个数
for(int i = 0 ; i < str.size() ; i++ ){
record[ str[i] - 'a' ]++ ;
}
string key = "" ;
for(int i = 0 ; i < 26 ; i++){
//拼接出key
if( record[i] != 0 ){
key += ( i + 'a' ) ;
key += to_string( record[i] ) ;
}
}
mapStr[ key ].push_back( str ) ;
}
for( auto &it : mapStr)
{
result.push_back( it.second ) ;
}
return result ;
}
};
438. 找到字符串中所有字母异位词
题目介绍:
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
提示:
1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母
思路:
方法一:
1、判断两个字符串长度,若字符串s的长度小于p的长度,返回空
2、创建vector容器countS ,countP,遍历字符串p,同时也从0开始,长度为p的长度遍历字符串s,获取p各个字母的出现次数,以及s的前lenP各个字母的出现次数
3、判断两个vector是否相等,相等则为异位词
4、建立从0开始,长度为lenP的滑动窗口,每次移动一位,向后移动lenS-lenP位,判断后续是否还存在异位词
具体代码如下:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
//滑动窗口
//之所以是vector而不是普通数组是因为vector有内置函数,可以直接比较两个容器是否相等
vector<int> countS(26) ;
vector<int> countP(26) ;
int lenS = s.size() ;
int lenP = p.size() ;
vector<int> result ;
if( s.size() < p.size() ) return {} ; //当字符串s的长度小于p的长度时,不存在子串
//获取p各个字母的出现次数,以及s的前lenP各个字母的出现次数
for( int i = 0 ; i < p.size() ; i++ )
{
++countP[ p[i] - 'a' ] ;
++countS[ s[i] - 'a' ] ;
}
if( countP == countS ) result.push_back(0) ; //两个数组相等则表明为异位词
for( int i = 0 ; i < lenS -lenP ; i++)
{
//滑动窗口向后移一位
--countS[ s[i] - 'a' ] ;
++countS[ s[i+lenP] - 'a'] ;
if( countP == countS ) result.push_back( i+1 ) ;
}
return result ;
}
};
方法二:
优化滑动窗口,不需要再每次比较两个数组是否相同,而是通过differ值(滑动窗口内字符串与p的不同字母或者相同字母数量不同的数量)来判断。
滑动窗口优化后具体代码如下:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
//滑动窗口优化
vector<int> count(26) ;
int lenS = s.size() ;
int lenP = p.size() ;
vector<int> result ;
int differ = 0 ; //滑动窗口内字符串与p的不同字母或者相同字母数量不同的数量
if( s.size() < p.size() ) return {} ; //当字符串s的长度小于p的长度时,不存在子串
//获取p各个字母的出现次数,以及s的前lenP各个字母的出现次数
for( int i = 0 ; i < p.size() ; i++ )
{
++count[ s[i] - 'a' ] ;
--count[ p[i] - 'a' ] ;
}
//判断两者differ值
for( int i= 0 ; i < 26 ; i++ )
{
if( count[i] != 0 ) ++differ ;
}
if( differ == 0 ) result.push_back(0) ; //当differ等于0时,表示两个字符串为字母异位词
for( int i = 0 ; i < lenS -lenP ; i++)
{
//移除窗口起始元素
//表示本来窗口内本字母的数量与p相同,但因为窗口后移,此字母数量将变得不同,differ加一
if( count[ s[i] - 'a'] == 0 ) ++differ ;
//表示本来窗口内本字母的数量与不同且正好多一个,但因为窗口后移,此字母数量将变得相同,differ减一
if( count[ s[i] - 'a'] == 1 ) --differ ;
//窗口后移,此字母数量减一
--count[ s[i] - 'a'] ;
//窗口新增元素
//表示本来窗口内本字母的数量与p相同,但因为窗口新增了此字母,此字母数量将变得不同,differ加一
if( count[ s[i+lenP] -'a' ] == 0 ) ++differ ;
//表示本来窗口内本字母的数量与不同且正好少一个,但因为窗口新增此字母,此字母数量将变得相同,differ减一
if( count[ s[i+lenP] -'a' ] == -1 ) --differ ;
//窗口后移,此字母数量加一
++count[ s[i+lenP] -'a' ] ;
if(differ == 0 ) result.push_back(i+1) ;
}
return result ;
}
};
349. 两个数组的交集
题目介绍:
给定两个数组 nums1 和 nums2 ,返回 它们的交集。
输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
思路:
1、创建unordered_set保存nums1的所有元素
2、遍历nums2,每遍历一个元素便判断set中是否包含此元素,若包含则是交集
具体代码如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result ; //用于存储结果
unordered_set<int> nums( nums1.begin() , nums1.end() ) ; //使用nums记录下num1所有不重复的元素
for( int num : nums2 )
{
//若nums中存在此元素,说明此元素是两者的交集
if( nums.find(num) != nums.end() ) result.insert( num ) ;
}
//转换为vector类型输出
return vector<int>(result.begin() , result.end() ) ;
}
};
202. 快乐数
题目介绍:
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例 2:
输入:n = 2
输出:false
提示:
1 <= n <= 231 - 1
思路:
1、先写一个方法获取每个数的平方之和
2、创建set来记录每次出现的平方之和
3、建立一个死循环,在循环内不断获取各位数的平方之和,直至出现和为1的情况或者出现set中已经记录的数字
具体代码如下:
class Solution {
public:
int getSum( int n ){
int sum =0 ;
while( n ){
sum += (n%10) * (n%10) ;
n = n/10 ;
}
return sum ;
}
bool isHappy(int n) {
unordered_set<int> set ; //用于保存已经出现过的数
while(1){
int sum = getSum(n) ; //获取各位数平方和
if(sum == 1 ) return true ; //若sum为1说明是快乐数,返回true
if( set.find(sum) != set.end() ) return false ; //若set中已经存在此数了,说明已经陷入了死循环,不是快乐数
else set.insert(sum ) ; //若set中不包含此数,将其添加
n = sum ;
}
}
};
1. 两数之和
题目介绍:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
思路:
1、创建map记录已经遍历过的数字,key为nums中的元素,value为该元素的下标
2、遍历nums,每遍历一个元素便判断map中是否包含target-nums[i],若存在则返回结果,不存在则向map中添加本次遍历的元素
具体代码如下:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int ,int> map ; //创建map记录已经遍历过的数字,key为nums中的元素,value为该元素的下标
for( int i = 0 ; i < nums.size() ; i++ ){
auto it = map.find( target - nums[i] ) ;
if( it != map.end() ) return { it->second , i } ; //若map中包含另一个数,返回i以及该数的下标
else map[ nums[i] ] = i ; //若map中不包含该数,将其添加
}
return {} ;
}
};
454. 四数相加 II
题目介绍:
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
示例 1:
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
示例 2:
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1
提示:
n == nums1.length
n == nums2.length
n == nums3.length
n == nums4.length
1 <= n <= 200
-228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228
思路:
1、创建map记录nums1+nums2所有的结果,key为和,value为出现过的次数
2、遍历nums1、nums2,记录所有的nums1 + nums2的结果
3、遍历nums3+nums4 ,若umap中存在 0-c-d 这个数,count加该数出现过的次数
具体代码如下:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int , int> umap ; //记录nums1+nums2所有的结果,key为和,value为出现过的次数
int count = 0 ;
//记录所有的nums1 + nums2的结果
for( int a : nums1){
for( int b : nums2 ){
++umap[ a+b ] ;
}
}
//遍历nums3+nums4
for( int c : nums3 ){
for( int d : nums4 ){
//若umap中存在 0-c-d 这个数,count加该数出现过的次数
if( umap.find( 0-c-d ) != umap.end() ) count += umap[0-c-d] ;
}
}
return count ;
}
};
15. 三数之和
题目介绍:
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-10^5 <= nums[i] <= 10^5
思路:
1、先对nums进行排序
2、遍历nums,判断nums[i] > 0 ,如果第一个元素就大于0,则等式不存在,直接返回空,并对a去重
3、创建左右指针,左指针为i+1,右指针指向nums.size()-1 , 而后便是双指针的循环,left<right。
4、还需注意对b和c进行去重
具体代码如下:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//双指针
vector<vector<int>> result;
sort(nums.begin() , nums.end() ) ; //先将其排序
//寻找a+b+c = 0
for(int i = 0 ; i < nums.size() ; i++ )
{
if( nums[i] > 0 ) return result ; //如果第一个元素就大于0,则等式不存在,返回空
if( i > 0 && nums[i] == nums[i-1] ) continue ; //对a去重
int left = i+1 , right = nums.size() - 1 ;
while( left < right )
{
//若三数之和大于0,right左移
if( nums[i] + nums[left] + nums[right] > 0 ) right-- ;
//若三数之和小于0,left右移
else if( nums[i] + nums[left] + nums[right] < 0 ) left++ ;
else{
result.push_back( {nums[i] , nums[left] , nums[right] }) ;
//对c去重
while (right > left && nums[right] == nums[right - 1]) right--;
//对b去重
while (right > left && nums[left] == nums[left + 1]) left++;
//找到第一组结果,左右指针继续移动
right-- ;
left++ ;
}
}
}
return result ;
}
};
18. 四数之和
题目介绍:
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
思路:
与上题三数之和的思路以及方法都是一样的,只是在剪纸去重时更为复杂,需要注意。
具体代码如下:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int> > result ;
sort(nums.begin() , nums.end()) ;
for( int i = 0 ; i < nums.size() ; i++)
{
//若第一个元素就大于target,直接退出此层循环,记得一定要加上条件nums[i] >=0
if( nums[i] > target && nums[i] >= 0 ) break ;
if( i > 0 && nums[i] == nums[i-1] ) continue ; //对a去重
for( int j = i +1 ; j < nums.size() ; j++ )
{
//若两个元素相加就大于target,直接退出此层循环,记得一定要加上条件nums[i] + nums[j] >= 0
if( nums[i] + nums[j] > target && nums[i] + nums[j] >= 0 ) break ;
//对b去重
if( j > i + 1 && nums[j] == nums[j-1] ) continue ;
int left = j +1 , right = nums.size() - 1 ;
while( left < right ){
//记得转成long类型,否则会溢出
//当和大于target时,右指针左移
if( (long)nums[i] + nums[j] + nums[left] + nums[right] > target ) right-- ;
//当和小于target时,左指针右移
else if( (long)nums[i] + nums[j] + nums[left] + nums[right] < target ) left++ ;
else{
result.push_back( { nums[i] , nums[j] , nums[left] , nums[right] }) ;
//去重
while( right > left && nums[right] == nums[right-1] ) right-- ;
while( right > left && nums[left] == nums[left+1] ) left++ ;
//得到一组结果后,左右指针移动
right-- ;
left++ ;
}
}
}
}
return result ;
}
};