问题描述
小R手中有一个由小写英文字母组成的字符串。她希望将这个字符串转换为回文字符串,并且要求字典序尽可能小。在这个过程中,小R最多可以更改字符串中的两个字符。每个字符可以被更改为任意的小写字母。现在你的任务是帮助小R构造出在满足条件的前提下字典序最小的回文字符串。
例如:对于字符串 acca,通过更改两个字符,可以得到回文字符串 aaaa,这是字典序最小的解。
解题思路
本题核心是在限定的操作次数内(最多两次),将给定字符串转换为字典序最小的回文串,需要考虑:
- 优先保证回文性: 首先需要通过替换操作使其变为回文串。
- 字典序最小化: 在保证回文性的基础上,尽可能将字符串的字典序最小化,可将字符替换为
a保证最小。 - 操作次数限制: 操作次数不能超过两次,并且尽可能一次替换影响两个对称位置的字符,最后再考虑奇数长度字符串。
具体步骤:
-
步骤1:构造回文串
从字符串的两端向中间遍历,比较对称位置的字符:
- 如果字符相同,无需操作。
- 如果字符不相同,需要进行替换,取两者中较小一个,计数一次操作。
-
步骤2:字典序最小化
再次从字符串的两端向中间遍历:
- 如果当前字符不是
a,且还有操作次数剩余,则将对称位置的字符同时替换为a,这样一次操作能影响两个字符。 - 如果字符串长度为奇数,且中间字符不是
a,且还有操作次数剩余,则将中间字符替换为a。
- 如果当前字符不是
代码实现
#include <bits/stdc++.h>
using namespace std;
string solution(string s)
{
int n = s.length();
int changes = 0;
// 检查是否回文串,如果不是,尽量变成回文串
for (int i = 0; i < n / 2; i++) {
if (s[i] != s[n - 1 - i]) {
changes++;
if (s[i] < s[n - 1 - i])
s[n - 1 - i] = s[i];
else
s[i] = s[n - 1 - i];
}
}
// 如果修改次数没超过 2 次,尽量改成a
for (int i = 0; i < n / 2 && changes < 2; i++)
if (s[i] != 'a' && changes < 1) { // 如果没修改过,一次性修改两个字符
s[i] = 'a';
s[n - 1 - i] = 'a';
changes += 2;
}
// 如果是奇数长度,且次数还没用完,中间字符改成a
if (n % 2 == 1 && changes < 2)
s[n / 2] = 'a';
return s;
}
int main()
{
cout << (solution("acca") == "aaaa") << endl;
cout << (solution("racecar") == "aacecaa") << endl;
cout << (solution("fecdef") == "feccef") << endl;
return 0;
}
代码解析
- 构造回文串:
- 遍历字符串的前一半,与后一半的对称位置比较。
- 如果字符不相同,选择较小的字符进行替换,确保回文性的同时使字典序尽可能小。
- 每次替换操作计数
changes加一。
- 字典序最小化:
- 在保证回文性的基础上,再次遍历字符串的前一半。
- 如果当前位置的字符不是
a,且剩余的操作次数允许,则将对称位置的字符同时替换为a。 - 每次这样的双字符替换操作计数
changes加二。
- 处理中间字符:
- 如果字符串长度为奇数,且还有剩余的操作次数,检查中间的字符。
- 如果中间字符不是
a,则将其替换为a,计数changes加一。
知识点总结
- 字符串操作: 熟练使用字符串的索引操作,访问和修改字符串中的字符。
- 双指针技巧: 在字符串处理中,常使用双指针从两端向中间遍历,用于处理回文串等对称结构的问题。
- 贪心策略: 在每一步选择中,做出当前最优的选择。为了保证回文且字典序最小,优先将字符替换为两者较小的一个,再替换为
a。
总结
通过这道题,学习了如何在有限的操作次数内,将字符串转换为字典序最小的回文串。核心在于结合回文串的特性和贪心策略,合理安排替换操作,使得在满足条件的前提下,得到最优解。该题还强化了我对字符串处理、双指针遍历以及算法优化的理解和应用。