时间:1s 空间:256M
题目描述:
有长度为 n 的字符串,仅包含小写字母。
小信想把字符串变成只包含一种字母。他每次可以选择一种字符 c,然后把长度最多为 m 的子串中的字符都替换成 c。
小信想知道最少需要操作几次能让字符串只包含一种字母。
输入格式:
第一行包含两个整数 n, m。
第二行包含一个长度为 n 的字符串,只有小写字符。
输出格式:
对于每组测试数据,输出一个整数表示答案。
样例1输入:
5 4
abcab
样例1输出:
1
样例2输入:
5 3
abcab
样例2输出:
2
约定与提示:
对于100%的数据,1≤m≤n≤2⋅105。
对于样例1:把子串 [1,4] 中的字符都变成 'b',或者把子串 [2,5] 中的字符都变成 'a'。
回答引自chatgpt
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200010;
int n, m;
char str[N];
int cnt[26];
int main()
{
cin >> n >> m >> str ;
int maxv = 0;
for (int i = 1, j = 0; i <= n; i ++ )
{
cnt[str[i] - 'a'] ++ ;
while (i - j > m || cnt[str[i] - 'a'] + m < i - j + 1)
{
cnt[str[j] - 'a'] -- ;
j ++ ;
}
maxv = max(maxv, i - j);
}
cout << n - maxv << endl;
return 0;
}
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
解题思路:
我们可以枚举所有的种类,以当前枚举到的种类为最后留下的字母,把所有跟它不同的字母换成它,用当前方案的操作步数,更新最优解 ans。
核心代码:
C++ 代码
如果我的回答解决了您的问题,请采纳!
参考GPT和自己的思路:这道题可以使用双指针的思路来解决,具体做法如下:
从左到右遍历字符串,维护一个区间 [l, r],表示以 s[r] 为结尾的长度最大为 m 的子串。
如果 s[r] 与 s[l] 不同,则需要将该区间中的所有字符替换成 s[r],并将区间向右移动一位,即令 l = r,r = r + 1。
如果 s[r] 与 s[l] 相同,则令 r = r + 1。
在遍历过程中,记录替换的次数,最后输出即可。
下面是 C++ 的代码实现:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2e5 + 10;
int n, m;
char s[N];
int main()
{
cin >> n >> m;
cin >> s;
int l = 0, r = 0;
int res = n;
while (r < n)
{
if (r - l + 1 <= m) res = min(res, max(1, r - l + 1) - (r - l + 1));
else
{
if (s[r] != s[l])
{
res = min(res, max(1, r - l) - (r - l));
l++;
}
else r++;
}
}
cout << res << endl;
return 0;
}
代码中,用 res 记录替换的次数,初始值为字符串长度 n,即最坏情况下每个字符都需要替换一次。
在遍历字符串的过程中,如果当前区间长度不超过 m,则直接将替换次数减去区间长度,即将该区间中的所有字符替换成 s[r]。
如果当前区间长度超过 m,则需要将左端点 l 向右移动一位,即将区间缩小,并将替换次数减去区间长度减一,因为除了 s[r] 以外的字符都需要替换成 s[r]。
遍历结束后,输出最终的替换次数即可。
参考GPT的回答和自己的思路,以下是满足你需求的实现代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2e5 + 10;
int n, m;
char s[N];
int main()
{
cin >> n >> m;
cin >> s + 1; // 从下标 1 开始读入字符串
int ans = n;
for (char c = 'a'; c <= 'z'; c++)
{
int cnt = 0, res = 0;
for (int i = 1; i <= n; i++)
{
if (s[i] == c) cnt++;
else
{
res += (cnt + m - 1) / m; // 计算需要操作的次数
cnt = 0;
}
}
res += (cnt + m - 1) / m;
ans = min(ans, res);
}
cout << ans << endl;
return 0;
}
代码思路:
首先读入字符串,并用一个变量 ans 记录当前的最小操作次数,初始化为字符串的长度。
然后枚举每一种可能的字符,对于每种字符,遍历一遍字符串,统计该字符在字符串中出现的次数,并计算需要多少次操作才能把该字符替换到字符串中的每个位置。
具体来说,我们用变量 cnt 统计当前位置之前该字符出现的次数,如果当前位置的字符不是该字符,就把 cnt 累加到答案中,并将 cnt 清零。
回答不易,还请采纳!!!
枚举每一个字母,用贪心法(遍历字符串,遇到与选择的字母相同的字母,跳过,否则覆写一次)计算覆写次数,求出最小值。
#include<stdio.h>
#include<set>
char str[200001];
int main(){
std::set<char> s;
std::set<char>::iterator it;
int n,m,i,j,min=(unsigned int)-1>>1;//min初始化为int类型最大值
//输入
scanf("%d%d\n",&n,&m);
for(i=0;i<n;i++){
str[i]=getchar();
s.insert(str[i]);//将输入的字符插入集合,用于确定字符串中存在哪些字符
}
str[i]='\0';
//外层循环遍历集合中所有字符
for(it=s.begin();it!=s.end();it++){
//循环内求出每种字符对应的覆写次数j
j=0;
for(i=0;i<n;i+=m,j++){//i+=m与j++结合,相当于覆写了一次
while(str[i]==*it)i++;//与选择的字符相同,跳过
if(i>=n)break;
}
if(j<min)min=j;
}
printf("%d",min);
}
测试样例