总结leetcode75中数组与字符串的算法题解题思路。
以下代码大部分为本人所写,少部分为官方示例代码。
题目:
给你两个字符串 word1 和 word2 。请你从 word1 开始,通过交替添加字母来合并字符串。如果
一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾。
返回 合并后的字符串 。
题解:
先用string.size()
计算出较短的长度wsize,
然后用for
循环把word1和word2各自的前wsize个字符交替合并,
接着用string.substr()
把长的那一串字符的剩余部分追加到末尾。
class Solution {
public:
string mergeAlternately(string word1, string word2) {
string nw="";
int w1size = word1.size();
int w2size = word2.size();
int wsize = min(w1size,w2size);
for (int i =0; i<wsize;++i){
nw += word1[i];
nw += word2[i];
}
if(wsize<w1size) nw +=word1.substr(wsize, w1size-wsize);
if(wsize<w2size) nw +=word2.substr(wsize, w2size-wsize);
return nw;
}
};
题目:
对于字符串 s 和 t,只有在 s = t + ... + t(t 自身连接 1 次或多次)时,我们才认定
“t 能除尽 s”。
给定两个字符串 str1 和 str2 。返回 最长字符串 x,要求满足 x 能除尽 str1 且 x
能除尽 str2 。
题解:枚举法
先用string.length()
计算出较短的长度i,
然后用for
循环把枚举每一个可能得长度,
如果i能够整除len1和len2,则用check
函数检查长度为i的这段字符串能够是不是str1和str2的公因子。
class Solution {
bool check(string t,string s){
int lenx = (int)s.length() / (int)t.length();
string ans = "";
for (int i = 1; i <= lenx; ++i){
ans = ans + t;
}
return ans == s;
}
public:
string gcdOfStrings(string str1, string str2) {
int len1 = str1.length(), len2 = str2.length();
for (int i = min(len1, len2); i >= 1; --i){ // 从长度大的开始枚举
if (len1 % i == 0 && len2 % i == 0){
string X = str1.substr(0, i);
if (check(X, str1) && check(X, str2)) return X;
}
}
return "";
}
};
题目:
给你一个数组 candies 和一个整数 extraCandies ,其中 candies[i] 代表第 i 个孩子拥有
的糖果数目。
对每一个孩子,检查是否存在一种方案,将额外的 extraCandies 个糖果分配给孩子们之后,此
孩子有 最多 的糖果。注意,允许有多个孩子同时拥有 最多 的糖果数目。
题解:
先用*max_element(candies.begin(), candies.end())
找到最大值max_num,
然后for
循环查看每个孩子加上extraCandies后是否大于max_num。
class Solution {
public:
vector<bool> kidsWithCandies(vector<int>& candies, int extraCandies) {
int clen = candies.size();
vector<bool> rev(clen,false);
int max_num = *max_element(candies.begin(), candies.end());
for (int i = 0; i < clen; ++i) {
if (candies[i] >= max_num - extraCandies)
rev[i] = true;
}
return rev;
}
};
题目:
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的
地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示
种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,
不能则返回 false 。
题解:贪心,为了种得多,需要在任意两朵花之间种上最多的花。
用prev
记录上一朵花种在哪里,初始时可以认为是位置-1,用count
记录可以种下几朵花。用for循环遍历每一个位置,如果找到一个位置上是有花的,则查看上一朵花是在哪,然后计算出两朵花之间能够种多少朵花。
情况1:遍历到第一朵花(prev<0)
,此时,只能在[0,i-2]上种花,总共有i-1个位置,
根据规则,可以种 ⌊ i / 2 ⌋ \lfloor i/2 \rfloor ⌊i/2⌋朵花(i为奇数:0,2,…i-3;i为偶数:0,2,…i-2)。
情况2:遍历到中间的某多花(prev>=0)且循环未结束
,此时,只能在[prev+2,i-2]上种花,总共有i-prev-3个位置,根据规则,种 ⌊ i − p r e v − 2 / 2 ⌋ \lfloor i-prev-2/2 \rfloor ⌊i−prev−2/2⌋朵花。
情况3:遍历完发现没有一个位置有花for循环结束且(prev<0)
,此时,能在[0,m-1]上种花,总共有m个位置,根据规则,可以种 ⌊ m + 1 / 2 ⌋ \lfloor m+1/2 \rfloor ⌊m+1/2⌋朵花。
情况4:遍历完后,发现最后一朵花后面还有空花坛for循环结束且(prev>=0)
,此时,只能在[prev+2,m-1]上种花,总共有m-prev-2个位置,根据规则,种 ⌊ m − p r e v − 1 / 2 ⌋ \lfloor m-prev-1/2 \rfloor ⌊m−prev−1/2⌋朵花。
class Solution {
public:
bool canPlaceFlowers(vector<int>& flowerbed, int n) {
int count = 0;
int m = flowerbed.size();
int prev = -1;
for (int i = 0; i < m; ++i) {
if (flowerbed[i] == 1) {
if (prev < 0) {
count += i / 2;
} else {
count += (i - prev - 2) / 2;
}
prev = i;
}
}
if (prev < 0) {
count += (m + 1) / 2;
} else {
count += (m - prev - 1) / 2;
}
return count >= n;
}
};
题目:
给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。
元音字母包括 'a'、'e'、'i'、'o'、'u',且可能以大小写两种形式出现不止一次。
题解:双指针
unordered_set
存储所有元音字母,unordered_set.count()
判断字符是否为元音。
用while
循环,分别从两头遍历,如果两边指针都是元音字母则用swap
调换它们,否则指针指向下一个。
class Solution {
public:
string reverseVowels(string s) {
unordered_set<char> mp = {
{'a'},
{'e'},
{'i'},
{'o'},
{'u'},
{'A'},
{'E'},
{'I'},
{'O'},
{'U'}
};
int i = 0, j = s.length()-1;
while(i<j) {
if (mp.count(s[i]) > 0 && mp.count(s[j]) > 0) {
swap(s[i], s[j]);
i++;
j--;
continue;
}
if (mp.count(s[i]) <= 0) {
i++;
// continue;
}
if (mp.count(s[j]) <= 0) {
j--;
// continue;
}
}
return s;
}
};
题目:
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位
整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
题解:左右乘积
先用vector
类型的left
存储每个位置左侧所有数的乘积。
然后for循环反向遍历
,用tmp记录每个位置右侧所有数的乘积,并把tmp乘以left[i]
得到位置i的结果。
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int numsize = nums.size();
vector<int> left(numsize, 1);
for (int i = 1; i < numsize; i++) {
left[i] = left[i - 1] * nums[i-1];
}
int tmp = 1;
for (int i = numsize-2; i >=0; i--) {
tmp *= nums[i+1];
left[i] *= tmp;
}
return left;
}
};
题目:
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
题解:贪心
用stack类型
的st3
记录递增序列,如果它长度flag
等于3则证明找到了。
情况1:如果有连续3个递增的数,那肯定能连续入栈st3,使其长度为3。
情况2:如果有连续2个递增a和b,然后出现一个c,它小于b大于a。则把b出栈,让c入栈,然后继续遍历。之所以这么做,是因为如果后续的元素中出现了某个元素e比c大,那就有ace符合条件,如果e大于b,由于c小于b那e同样大于c。所以把b出栈,让c入栈是合理的。
情况3:如果有连续2个递增a和b,然后出现一个c,它小于a和b。先用一个变量max2
(初始化时赋值为INT_MAX
)记录下b的值,然后把a和b都出栈,让c入栈。继续遍历。之所以要用max来记录b的值,是因为如果后续有一个变量e大于b(也大于c),那么实际上abe已经满足条件了, 但我们把它出栈了,导致现在栈里只有c一个,ce的长度只有2反而不满足。所以用max2来记录下这种特殊情况。
情况4:如果当前遍历的值e大于max2,则证明之前存在两个递增数a和b,且a 情况5:如果当前遍历的值e大于栈顶元素且栈中已经有两个元素了,则证明有3个递增数,满足条件。
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int numsize = nums.size();
if (numsize < 3) return false;
int flag = 1;
stack<int> st3;
st3.push(INT_MAX);
int max2 = INT_MAX;
for (int i = 0; i < numsize; ++i) {
if (nums[i] > st3.top()) {
flag++;
if (flag == 3|| max2 < nums[i]) return true;
st3.push(nums[i]);
}
else if (nums[i] < st3.top()) {
if(flag ==2) max2 = st3.top();
while (!st3.empty()&& nums[i] <= st3.top()) {
st3.pop();
flag--;
}
st3.push(nums[i]);
flag++;
}
}
return false;
}
};
题目:
给你一个字符数组 chars ,请使用下述算法压缩:
从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符 :
如果这一组长度为 1 ,则将字符追加到 s 中。
否则,需要向 s 追加字符,后跟这一组的长度。
压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars 中。需要注意的是,
如果组长度为 10 或 10 以上,则在 chars 数组中会被拆分为多个字符。
请在 修改完输入数组后 ,返回该数组的新长度。
你必须设计并实现一个只使用常量额外空间的算法来解决此问题。
题解:双指针
用lenc记录已压缩子串的长度,用pre记录上一个位置是哪种字符,用lennow记录pre字符的长度。
当chars[i]!=pre
时,证明有新的字符了,计算下lennow的长度(对应的字符表示暂存于tmp
中),然后将pre和对应长度存入chars的相应位置(lenc)中。
当chars[i]==pre
时,lennow+=1
。
for循环结束后,需要将最后一个字符及其长度也压缩存入chars
中。
class Solution {
public:
int compress(vector<char>& chars) {
int lenc = 0, chlen = chars.size(), lennow = 1;
char pre = chars[0];
string tmp = "";
for (int i = 1; i < chlen; ++i) {
if (pre == chars[i]) {
lennow++;
}
else {
chars[lenc++] = pre;
if (lennow>1){
while (lennow > 0) {
tmp += to_string(lennow % 10);
lennow /= 10;
}
for (int j = tmp.size() - 1; j >= 0; --j) {
chars[lenc++] = tmp[j];
}
tmp="";
}
pre = chars[i];
lennow = 1;
}
}
chars[lenc++] = pre;
if (lennow > 1) {
while (lennow > 0) {
tmp += to_string(lennow % 10);
lennow /= 10;
}
for (int j = tmp.size() - 1; j >= 0; --j) {
chars[lenc++] = tmp[j];
}
}
return lenc;
}
};
a. 涉及字符、数组的题,往往都是需要for循环遍历的。
b. 考察的点一般是如何减少遍历复杂度,比如不要使用嵌套for循环。
c. 有时候也会考察你是否能够在原数组上做改动或者只定义1个新的数组而不是2个来实现,比如第8题要求只能在原数组上修改,不能定义新的O(n)级别的变量。还有第6题本来是可以用一个left和一个right数组来分别记录左累乘和右累乘,然后用一个rev数组来计算最终结果的,但为了减低空间复杂度,可以只用一个left数组和一个right变量来实现。
d. 常用方法:贪心、双指针。