一、题目需求:
小R手中有一个由小写英文字母组成的字符串。她希望将这个字符串转换为回文字符串,并且要求字典序尽可能小。在这个过程中,小R最多可以更改字符串中的两个字符。每个字符可以被更改为任意的小写字母。现在你的任务是帮助小R构造出在满足条件的前提下字典序最小的回文字符串。
例如:对于字符串 acca,通过更改两个字符,可以得到回文字符串 aaaa,这是字典序最小的解。
测试样例
样例1:
输入:
s = "acca"输出:'aaaa'
样例2:
输入:
s = "racecar"输出:'aacecaa'
样例3:
输入:
s = "fecdef"输出:'feccef'
二、实现思路
首先我们需要了解一下相关概念
- 回文数:简单来说就是从头读到尾以及从读尾到头的结果是一样的,比如“101”“aba“等
- 字典序最小:在字符串对比中,对比规则为:若字符串长度不同,则短的字符串对应字典序较小;若字符串长度相同,是根据每一索引处的字符大小来决定字典序的,例如“abc”与"abcd"相对比,"abc"字典序较小;"aae"与“abc"对比时,1索引处前一字符串对应字符较小,故前面的字符串对应字典序较小
2.1 回文数的构造实现
需要构造一个回文数,那么就需要我们先对字符串是否是回文数进行一个判定
一般来说我们可以利用String中的reverse()方法来进行equal判断
String strS="abb";
StringBuilder sb=new StringBuilder(strS);
String strF = sb.reverse().toString();
System.out.println(strS.equals(strF));//输出结果为false
但在本体中,并没有给出不能构成回文数的情况应该返回什么值,所以可以认为最终一定是可以输出一个回文数的,故在最后环节不做回文判断,现在让我们直接来进行构造
构造过程我们采用双指针法,其中要注意的是,本题已经注明只能更改两个字符,故我们设置chance=2,在使用双指针时,注意左右索引不要超出范围。
StringBuilder sbl=new StringBuilder();
StringBuilder sbr=new StringBuilder();
int chance=2;
int leftIndex=0;
int rightIndex=s.length()-1;
char flag=' ';//标记外侧元素
String result=s;
以上为初始变量
- 其中sbl和sbr用于储存左右两个指针已读取的安全数据(无需修改的)
- leftIndex和rightIndex为左右指针
- flag用于标记外侧元素(具体是干什么后面会说)
- result用于最后输出,这里传递了一个s的初始值
while(leftIndex<rightIndex&&chance>0){
if (s.charAt(leftIndex)==s.charAt(rightIndex)){
sbr.append(s.charAt(rightIndex));
sbl.append(s.charAt(leftIndex));
leftIndex++;
rightIndex--;
}else{
int diff = s.charAt(leftIndex) - s.charAt(rightIndex);
if (diff>0){
//左边大
sbl.append(s.charAt(rightIndex));
sbr.append(s.charAt(rightIndex));
if (flag==' '){
flag=s.charAt(rightIndex);
}
leftIndex++;
rightIndex--;
}else {
//右边大
sbl.append(s.charAt(leftIndex));
sbr.append(s.charAt(leftIndex));
if (flag==' '){
flag=s.charAt(leftIndex);
}
leftIndex++;
rightIndex--;
}
chance--;
}
if (leftIndex==rightIndex){
sbl.append(s.charAt(leftIndex));
}
}
在循环中大概可以分为以下几类情况
1.左指针与右指针所指向的元素内容一致:这时候需要将数据分别存入sbl与sbr,指针对应进行加减
if (s.charAt(leftIndex)==s.charAt(rightIndex)){
sbr.append(s.charAt(rightIndex));
sbl.append(s.charAt(leftIndex));
leftIndex++;
rightIndex--;
}
2.左指针与右指针所指向的元素内容不一致:对指针元素进行差值判断,若左边小则将右边元素改为左侧元素,同理若右边小,则将左边改为右边元素,再进行数据存储与指针加减
else{
int diff = s.charAt(leftIndex) - s.charAt(rightIndex);
if (diff>0){
//左边大
sbl.append(s.charAt(rightIndex));
sbr.append(s.charAt(rightIndex));
if (flag==' '){
flag=s.charAt(rightIndex);
}
leftIndex++;
rightIndex--;
}else {
//右边大
sbl.append(s.charAt(leftIndex));
sbr.append(s.charAt(leftIndex));
if (flag==' '){
flag=s.charAt(leftIndex);
}
leftIndex++;
rightIndex--;
}
chance--;
}
那么在进行遍历处理后,我们会得到两个填充完成的sbl与sbr对象,这时候我们只需要把sbr进行reverse()操作后再将二者进行拼接,即可得到一个我们所需要的字符串。
然而——这只是我们最理想的情况,但是在更多的时候,chance是没有被用完的,这就代表字符串可能存在:已经是回文字符串,但并非最小字典序的情况
可以根据chance的不同分为以下几种情况
1.chance=2:证明在双指针中并未对字符进行操作,即输入时已是回文字符串,例:“bffb”,这种情况我们只需要找到最外层的字符,将外层字符变换为最小的‘a’字符即可
if (chance==2){
leftIndex=0;
rightIndex=result.length()-1;
while (chance>0){
if (result.charAt(leftIndex)!='a'){
if (rightIndex>leftIndex) {
result = result.substring(0, leftIndex) + 'a' + result.substring(leftIndex + 1, rightIndex)
+ 'a' + result.substring(rightIndex + 1);
chance = 0;
}else {
result = result.substring(0,leftIndex)+'a'+result.substring(leftIndex+1);
chance = 0;
}
}
leftIndex++;
rightIndex--;
}
2.chance=1:证明在双指针中对字符进行了一次变换操作,例:"abca"→"abba"这时候我们相应的进行操作即可
if (chance==1){
for (int i = 0; i < result.length(); i++) {
if (result.charAt(i)!='a'){
if (result.charAt(i)==flag){
chance=2;
break;
}else {
break;
}
}
}
if(result.length()%2==1){
int mid=(result.length()+1)/2;
if (result.charAt(mid-1)!='a'){
result= result.substring(0,mid-1)+'a'+result.substring(mid);
chance--;
}
}
}
最后我们将我们存入的result字符串进行输出就为正确答案啦,但是也要注意比如字符索引的限制,字符串的长度等等问题,希望你也能找到最优解!