题目描述
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例 1:
输入: “aacecaaa”
输出: “aaacecaaa”
示例 2:
输入: “abcd”
输出: “dcbabcd”
求解
什么是回文串?回文串就是指正着看和反过来看都是相同的字符串,即字符串对称。回文分为单回文和双回文,单回文字符串长度是奇数,这个字符串是以某个字符为中心对称的;双回文字符串长度是偶数,它并不以某个字符为中心。
初步分析:这题需要在前面补字符以达到对称,求最短回文串,最初我采用了选定某点然后向四周扩散补全的思路。具体实现的方法是把求解分为三个部分:求单回文奇数,单回文偶数,双回文,一般循环到字符串长度的一半就可以了,单回文在奇数和偶数下的截止位置不同,故分开讨论,最后总共需要三个循环。在循环中,两个指针从某点出发向两边运动,如果两个指针对应字符相等,则继续向两边运动,一旦low指针小于0,则开始把high指针对应的字符插入0位置,循环往复,知道high指针也运动到了字符串的最右侧。在不断获取回文串的过程中更新最短回文串并返回。
程序:
class Solution {
int length=10000000;
String str="";
public String shortestPalindrome(String s) {
if(s.length()%2==1){
for(int i=0;i<s.length()/2+1;i++){
System.out.println(i);
StringBuilder stringBuilder=get(s, i, i);
if(stringBuilder!=null&&stringBuilder.length()<length){
str=stringBuilder.toString();
length=stringBuilder.length();
}
}
}else{
for(int i=0;i<s.length()/2;i++){
System.out.println(i);
StringBuilder stringBuilder=get(s, i, i);
if(stringBuilder!=null&&stringBuilder.length()<length){
str=stringBuilder.toString();
length=stringBuilder.length();
}
}
}
for(int j=0;j<s.length()/2;j++){
StringBuilder stringBuilder2=get(s,j,j+1);
if(stringBuilder2!=null&&stringBuilder2.length()<length){
str=stringBuilder2.toString();
length=stringBuilder2.length();
}
}
return str;
}
public StringBuilder get(String s,int low,int high){
StringBuilder sBuilder=new StringBuilder(s);
while(high<sBuilder.length()){
if(low>=0&&sBuilder.charAt(low)!=sBuilder.charAt(high)){
return null;
}else if(low>=0&&sBuilder.charAt(low)==sBuilder.charAt(high)){
low--;
high++;
}else if(low<0){
sBuilder.insert(0, sBuilder.charAt(high));
high+=2;
}
}
return sBuilder;
}
}
结果:在最后一个“aaaa…aaaaaa”的测试案例中,时间超了,没有通过测试。代码时间复杂度太大,需要优化。回看代码,代码太过冗杂,用了3个循环!
重新整理思路:求解的最糟糕的情况是把字符串反转然后拼接在前面,比较好的情况是字符串的前面一部分存在对称,只要补充少数字符即可。因此,新的思路就是先把字符串反转得到一个反转字符串,然后在其中找到和原字符串中相同的部分。举个例子:
原字符串:acbca ab
反转:ba acbca
重叠部分:acbca
拼接:ba acbca +ab(反转串加上原字符串中的后两个字符)
重点就是反转后找相同的部分,然后拼接。
代码如下:
class Solution {
public String shortestPalindrome(String s) {
if(s.length()<=1){
return s;
}else{
String string=new StringBuilder(s).reverse().toString();
for(int i=0;i<s.length();i++){
if(string.substring(i,string.length()).equals(s.substring(0, string.length()-i))){
return new StringBuilder(string).append(s.substring(s.length()-i,s.length())).toString();
}
}
return new StringBuilder(string).append(s).toString();
}
}
}
代码简洁了许多,只用到了一个for循环。代码也通过了Leecode的编译。
一个小细节:在字符串操作中,使用的是StringBuilder,它相比String和StringBuffer速度最快。String是存在在常量池中的,每次修改都要回收之前的垃圾并且在常量池中添加新的字符,很费时间;StringBuffer和StringBuilder基本的功能是一样的,但是StringBuffer因为是线程安全的,这限制了它的速度。所以在字符串操作中我们一般采用StringBuilder。