题干: https://leetcode.com/problems/shortest-palindrome/
虽说是hard模式,这题还真没给我什么hard的感觉。这种回文的问题,多在纸上画画比较利于分析,尤其是通过一种近乎几何直觉的方式会更有帮助。
比如给一个字符串aacecaaa,让你左边添加一些字符,变成回文,啥叫回文?就是轴对称嘛,那么就意味着这个补齐后的字符串如果是奇数长度,必然有一个字符是对称轴,如果是偶数长度,必然有一个字符与字符之间的槽(或者叫缝隙吧)是对称轴。
但是到底补齐之后的串是偶数还是奇数呢?不知道,于是最好分类讨论一下,把两种情况的解都找出来,然后取短者即可。
我就以奇数长度为例子做个说明,偶数长度的思想也不过大同小异。
明确一个目标,就是找对称轴位置,只要找到了对称轴,那么剩下的不过是一些简单的补齐操作而已。
那么对称轴位置需要满足什么条件呢?
假设某个index的字符为对称轴,那么可以想象,这个对称轴的位置必然在整个现有字符串(即还没有添加前缀的原始字符串)的左半部分(包括正中)位置,这样我们才能在左边补字符时补到对称,当然最好的case是我们啥也不补就已经对称,那么这个对称轴位置必然在原始字符串的正中间字符串。说上面这一点只是给对称轴定了个粗略的范围,然后我们要尽量在这个范围内找靠右的位置做对称轴。因为对称轴越靠右,左边就越能少补字符串。接下来就是评估哪些位置可以做对称轴,哪些不能做了。
也容易想到,如果某个index左右等距的两个字符已经出现了不等,那么这个index就不可能是对称轴,根据这个原则,我们就可以排除不合法的对称轴候选项。最终得出可能的最靠右的对称轴位置。找到后,将对称轴右侧的子串进行翻转,再跟对称轴以及其右边子串整体拼接成一个回文返回。
偶数串的思路大同小异,这里就不再赘述,下面看代码:
package com.example.demo.leetcode;
public class ShortestPalindrome {
public String shortestPalindrome(String s) {
if(s.length()<1){
return s;
}
String jishuShortest = jishu(s);
String oushuShortest = oushu(s);
if(jishuShortest.length()<oushuShortest.length()){
return jishuShortest;
}else{
return oushuShortest;
}
}
private String oushu(String s){
//以rightMostPossibleBeforeIndex为下标的元素的前面的slot为对称轴
int rightMostPossibleBeforeIndex = (s.length()-1)/2;
int correctBeforeIndex = rightMostPossibleBeforeIndex;
for(int i=rightMostPossibleBeforeIndex;i>=0;i--){
boolean slotDuichen = checkSlotDuichen(s, i);
if(slotDuichen){
correctBeforeIndex = i;
break;
}
}
return doOushuPrefix(s, correctBeforeIndex);
}
private String jishu(String s){
int rightMostPossibleIndex = (s.length()-1)/2;
int duichenIndex = 0;
for(int i=rightMostPossibleIndex;i>=0;i--){
boolean duichen = checkDuichen(s, i);
if(duichen){
duichenIndex = i;
break;
}
}
return doPrefix(s, duichenIndex);
}
String reverseString(String s){
return new StringBuilder(s).reverse().toString();
}
private String doPrefix(String s, int duichenIndex){
String rightPart = s.substring(duichenIndex);
String leftPart = reverseString(s.substring(duichenIndex+1));
return leftPart+rightPart;
}
private String doOushuPrefix(String s, int slotIndex){
String rightPart = s.substring(slotIndex);
String leftPart = reverseString(rightPart);
return leftPart+rightPart;
}
private boolean checkDuichen(String s, int midIndex){
if(midIndex==0){
return true;
}
for(int j=midIndex-1;j>=0;j--){
if(s.charAt(j)!=s.charAt(2*midIndex-j)){
return false;
}
}
return true;
}
private boolean checkSlotDuichen(String s, int slotIndex){
if(slotIndex==0){
return true;
}
for(int j=slotIndex-1;j>=0;j--){
if(s.charAt(j)!=s.charAt(2*slotIndex-j-1)){
return false;
}
}
return true;
}
public static void main(String[] args) {
String orgin = "";
ShortestPalindrome demo = new ShortestPalindrome();
String ret = demo.shortestPalindrome(orgin);
System.out.println(ret);
}
}