1.数的乘积等于两数乘积的方法数
题目
解析
1.解析
- 先将数组排序,然后针对第一个数组每个数进行遍历,第二个数组用双指针计算数量;
- 这题很容易爆 int,所以要用 long / long long 存储平方数或乘积;
- 如果小于目标值,直接 L++;大于目标值,直接 R--;
- 如果等于目标值,且左数等于右数,说明 L-R 均相等,计算 C(R - L + 1, 2) 即可;
- 如果左数不等于右数,则左右指针在等于前数情况下,分别向内移动,计算 (L1 - L) * (R1 - R);
- 优化:最大数平方小于目标,直接 break;最小数平方大于目标,直接 continue;
- 时间复杂度:O(m*n);空间复杂度:O(1);
代码
class Solution {
public:
int numTriplets(vector<int>& nums1, vector<int>& nums2) {
// 时间复杂度:O(mn)
// 空间复杂度:O(1)
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
return cal(nums1, nums2) + cal(nums2, nums1);
}
int cal(vector<int>& nums1,vector<int>& nums2){
int ans = 0;
for(int i = 0;i < nums1.size();i ++){
long target = (long)nums1[i] * nums1[i];// 第一个数组目标乘积
int l = 0,r = nums2.size() - 1;
// 不满足情况: 最大乘积小于目标 / 最小乘积大于目标
if ((long)nums2[r] * nums2[r] < target) break;
else if ((long)nums2[l] * nums2[l] > target) continue;
// 双指针
while(l < r){
long s = (long)nums2[l] * nums2[r];// 第二个数组计算乘积
if(s > target) r --;
else if(s < target) l ++;
else {
if(nums2[l] == nums2[r]){ // l - r 都相等,任意两个数相乘都满足
ans += (r - l + 1) * (r - l) / 2;
break;
}else{
int cnt1 = 0,x = nums2[l];
int cnt2 = 0,y = nums2[r];
while(l <= r && nums2[l] == x){ // 注意边界问题
cnt1 ++;
l ++;
}
while(l <= r && nums2[r] == y){
cnt2 ++;
r --;
}
ans += cnt1 * cnt2;
}
}
}
}
return ans;
}
};
2.三数之和的多种可能
题目
解析
1.解析
- 这题就是《三数之和》及上面这题的结合版,用双指针,讨论左数与右数的关系;
- 左数等于右数,说明 L-R 均相等,计算 C(R - L + 1, 2) 即可;
- 如果左数不等于右数,则左右指针在等于前数情况下,分别向内移动,计算 (L1 - L) * (R1 - R);特别注意:指针移动的循环一定要加上 L <= R;
- 时间复杂度:O(n ^ 2);空间复杂度:O(1);
代码
class Solution {
public:
int threeSumMulti(vector<int>& arr, int target) {
// 时间复杂度:O(n ^ 2)
// 空间复杂度:O(1)
sort(arr.begin(),arr.end());
int n = arr.size();
int ans = 0;
int mod = 1e9 + 7;
for(int i = 0;i < n-2;i ++){
int x = target - arr[i]; // 转换为双指针问题
int l = i + 1,r = n-1;
while(l < r){
if(arr[l] + arr[r] < x) l ++;
else if(arr[l] + arr[r] > x) r --;
else{
if(arr[l] == arr[r]) { // l - r 都相等,任意两个数相加都满足
ans = (ans + (r - l + 1) * (r - l) / 2) % mod;
break;
}
else{
int cnt1 = 0, x = arr[l];
int cnt2 = 0, y = arr[r];
while (l <= r && x == arr[l]) { // 注意边界问题
cnt1++;
l++;
}
while (l <= r && y == arr[r]) {
cnt2++;
r--;
}
ans = (ans + cnt1 * cnt2) % mod;
}
}
}
}
return ans;
}
};
3.令牌放置
题目
解析
1.解析
- 根本目的:尽可能得分,也就是尽可能多的翻得分牌;
- 注意:没有得分牌 / 积分不够翻最小的得分牌时,返回0;
- 有积分且足够翻得分牌,则直接翻得分牌;积分不够,就判断有没有得分,若有得分,就消耗1得分翻最大积分牌;就这样不断循环,只要翻了得分牌,就更新最大得分;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
int bagOfTokensScore(vector<int>& tokens, int power) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
sort(tokens.begin(),tokens.end());
int ans = 0,score = 0;
int n = tokens.size();
// 没有得分牌可翻/积分不够翻最小的得分牌
if(tokens.empty() || power < tokens[0]) return 0;
int l = 0,r = n-1;
// 双指针,根本目标:翻更多的得分牌
while(l <= r){
if(power >= tokens[l]) { // 积分足够翻得分牌
power -= tokens[l];
l ++;
score ++;
ans = max(ans,score);
}else {
if(score >= 1){ // 积分不够翻得分牌,就看又没有得分去翻积分牌
power += tokens[r];
r --;
score --;
}
}
}
return ans;
}
};
4.分割两个字符串得到回文串
题目
解析
1.暴力解析:创建两个字符串存储拼接结果,内存超了;
2.解析
- 分别设置两个指针在数组 a, b 的首端,尾端,向内不断扩展直到两数不相等;
- 设此时分别指向 L,R,则 0 - L 和 R - (n-1) 一定能构成回文串,接下来就是判断 L-R 这一段字符是否为回文串;
- 第一个两种情况,分别为数组 a,b 提供中间字符段,只要一个满足是回文串就返回 true;
- 第二个两种情况,分别为数组 a,b 提供前 / 后字符段,满足一个也返回 true;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
// 判断 s[i] 到 s[j] 是不是回文串
bool isPalindrome(string &s,int i,int j){
while(i <= j && s[i] == s[j]){
i ++;
j --;
}
return i > j;
}
// 判断数组a 的前段序列和数组b 的后段序列能否构成回文串
bool check(string &a,string &b){
int l = 0,r = a.size()-1;
// 找到 a 和 b 数组可以匹配的最长字符段
while(l < r && a[l] == b[r]){
l ++;
r --;
}
// 剩余部分 l - r 由数组a / 数组b 其中一个给出,判断是否为回文串
return isPalindrome(a,l,r) || isPalindrome(b,l,r);
}
bool checkPalindromeFormation(string a, string b) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
return check(a,b) || check(b,a);// 考虑哪个数组提供前序列
}
};