C++日常刷题积累
今日刷题汇总 - day026
1、小红的ABC
1.1、题目
1.2、思路
读完题,知道让求只包含 ‘a’ , ‘b’ , ‘c’ 三种字符的字符串中的最短回文子串,其中子串要求是连续的。如果不存在长度超过1的回文子串,则输出-1。否则输出长度超过1的最短回文子串的长度。那么分析示例和题目,既然只有三个字符,那么最短的情况下,以a,b,b,a,c为例,bb最短为2,以a,b,a,b,c为例,aba最短为3,以,a,b,c,c,b,a为例,最短cc为2,以a,b,c,a,b,c,不存在最短输出-1即可,所以最短只有两种情况,要么长度为2,要么长度为3。所以只需要遍历字符串判断是否满足即可。那么接下来,就是程序实现。
1.3、程序实现
首先,按照题目和思路分析遍历字符串str即可。值得注意的是一些细节的处理,直接初始化ret = -1,兼容不是回文的情况,然后判断时注意越界控制,以及遍历逻辑的控制即可。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin >> str;
int len = str.size();
int ret = -1;//置-1,考虑str不是回文的情况
for(int i = 0;i < len;i++)
{
//子串长度为2
if(i+1 < len && str[i] == str[i+1])
{
ret = 2;
break;
}
else if(i+2 < len && str[i] == str[i+2])//子串长度为3
{
ret = 3;
//这里不需要break;因为遍历到ret = 3,可能遍历到后面存在ret = 2的情况。
}
}
cout << ret << endl;
return 0;
}
2、不相邻取数
2.1、题目
2.2、思路
读完题知道,对于一个数组,使得从中取出来的不相邻的数之和尽可能大。根据题目和示例,分析比如对于 2,8,4,5,6,选择8+6也满足不相邻的条件,那么蛮力的思想就是从左往右重复遍历求最大和,那么属于是线性dp进行优化,与之前删除相邻数字的最大分数思路类似,采用线性dp那么就需要确定状态表示和状态转移方程,如图所示:
那么接下来,就是程序实现。
2.3、程序实现 – dp
根据思路分析,总结状态表示和状态转移方程:
状态表示两种情况:
a、f[i]:表示从前 i 个数挑选,最后一个位置的数必选,此时的最大和;
b、g[i]:表示从前 i 个数挑选,最后一个位置的数不选,此时的最大和;
状态转移方程:
a、f[i]:f[i] = g[i-1] + arr[i];
b、g[i]:g[i] = max(f[i-1],g[i-1]);
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int arr[N];
int f[N], g[N];
int main()
{
int n;
cin >> n;
for(int i = 1;i <= n;i++)
cin >> arr[i];
for(int i = 1;i <= n;i++)
{
f[i] = g[i-1] + arr[i];
g[i] = max(f[i-1],g[i-1]);
}
cout << max(g[n],f[n]) << endl;
return 0;
}
3、空调遥控
3.1、题目
3.2、思路
读完题知道,掌控遥控器使得集训室的队员要求的温度尽可能满足多能够进入训练状态,且满足当室内温度为K时,|a[i] - K| <= p,求在最佳情况下,最多有多少队员同时进入训练状态。为了方便理解画个图:
那么根据示例和分析,思考蛮力法思路就是枚举所有可能但是会超时,所以就得想办法优化,如图所示:
3.3、程序实现 – 二分法
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int len(vector<int>& ret, int left, int right, int p, int k)
{
int left_min = left, right_min = right;
int left_max = left, right_max = right;
// 寻找最小差值不超过p的左边界
while (left_min < right_min)
{
int mid = left_min + (right_min - left_min) / 2;
if (k - ret[mid] <= p)
{
right_min = mid;//说明中点或中点左侧可能有符合条件的元素,因此将 right_min 更新为 mid
}
else
{
left_min = mid + 1;//说明中点及其右侧的元素都不可能是左边界,因此将 left_min 更新为 mid + 1。
}
}
// 寻找最大差值不超过p的右边界
while (left_max < right_max)
{
//这里的 +1 是为了避免在 left_max 和 right_max 相等时导致死循环
int mid = left_max + (right_max - left_max + 1) / 2;
if (ret[mid] - k <= p)
{
left_max = mid;//说明中点或中点右侧可能有符合条件的元素,因此将 left_max 更新为 mid
}
else
{
right_max = mid - 1;//说明中点及其右侧的元素都不可能是右边界,因此将 right_max 更新为 mid - 1
}
}
return left_max - right_min + 1;
}
int main() {
int n, p;
cin >> n >> p;
vector<int> ret(n);
for (int i = 0; i < n; i++)
cin >> ret[i];
sort(ret.begin(), ret.end());
int temp = 0;
int min_val = ret[0] - p;
int max_val = ret[n - 1] + p;
for (int i = min_val; i <= max_val; i++)
{
temp = max(temp, len(ret, 0, n - 1, p, i));
}
cout << temp << endl;
}
3.4、程序实现 – 滑动窗口
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int arr[N];
int main()
{
int n,p;
cin >> n >> p;
for(int i = 0;i < n;i++)
cin >> arr[i];
//排序
sort(arr,arr+n);
int left = 0;
int right = 0;
int ret = 0;
while(right < n)
{
//进窗口,判断窗口,滑动窗口
while(arr[right] - arr[left] > 2*p)
{
left++;
}
//更新结果
ret = max(ret,right - left + 1);
right++;
}
cout << ret << endl;
return 0;
}