题目描述:
字符串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; }