题目描述:
字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?
输入描述:
第一行为一个字符串S与一个非负整数m。(1 <= |S| <= 1000, 1 <= m <= 1000000)
输出描述:
一个非负整数,表示操作之后,连续最长的相同字母数量。
示例:
输入
abcbaa 2
输出
2
说明
使2个字母a连续出现,至少需要3次操作。即把第1个位置上的a移动到第4个位置。
所以在至多操作2次的情况下,最多只能使2个b或2个a连续出现。
解析:
结合之前的题,现在应该明白读混合多种类的数据不需要直接整串读了,我们应该先把他们挑出来,归类,分成各字母数组再读取。
此后,对于各字母数组,假设都能聚拢,那应当选取其中一个字母为聚拢点,两旁的字母往中间移动,因此我们需要一个类似左右指针的东西确定移动的边界,在比较两旁的距离选较小的一边先聚拢。
代码:
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
int main()
{
//读入s和n
int n;
string s;
cin >> s >> n;
//按字母归类存储每个字母出现的位置
map<char, vector<int>> m;
for(int i=0;i<s.size();i++)
{
char ch = s[i];
m[ch].push_back(i);
}
//答案结果至少为1
int ans = 1;
//每个字母数组遍历
for(int i=0;i<26;i++)
{
//当前字母为
char ch = (char)('a' + i);
//获取当前字母数组长度
int size = 0;
size = m[ch].size();
//数组长度只有0或1可以直接跳过
if(size<=1)
{
continue;
}
//字母数组每个位置陆续作为聚拢中心
for(int j=0;j<size;j++)
{
//每次选新的聚拢中心应当更新可用步长和计数
int feet = n;
int count = 1;
//备选的字母,数字为字母数组的下标
int l_num = j - 1;
int r_num = j + 1;
//左右应该被移动到的位置
int left = m[ch][j] - 1;
int right = m[ch][j] + 1;
//开始聚拢
while (true)
{
//左右移动的距离
//这里坑了我好几天才发现步长上限不是1000 UωU~~
int dis_l = 9999999;
int dis_r = 9999999;
//当l_num处于数组中时,可选
if(l_num>=0)
{
dis_l = left - m[ch][l_num];
}
//当l_num处于数组中时,可选
if (r_num < size)
{
dis_r = m[ch][r_num] - right;
}
//当可用步长双方都不足时,则跳出循环
if (feet < dis_l && feet < dis_r)
{
break;
}
//选距离小的
if(dis_l<=dis_r)
{
//更新步长与计数
feet -= dis_l;
count++;
//更新下一个备选位置和备选字母
l_num--;
left--;
}
else
{
//更新步长与计数
feet -= dis_r;
count++;
//更新下一个备选位置和备选字母
r_num++;
right++;
}
//更新最大值
ans = max(ans, count);
}
}
}
cout << ans << endl;
}