最长回文子串
最长回文子串一般有以下两种情况:
问题描述1:给定一个字符串,求它的最长回文子串的长度,并且回文子串的字符在原字符串中必须连续。
分析:很明显可以使用暴力法求解,枚举出所有的子串,分别判断其是否为回文。但是这种方法效率比较低,而且时间复杂度太高,并且如果一个长的子串包含另一个短一些的子串,那么对子串的回文判断其实是不需要的,等等。
我们可以对其进行优化,因为回文串是以中间的字符为中心左右对称的,例如回文串“aba”,以b为中心,它的左端和右端对称,都是a。
所以我们可以枚举中心位置,从中心位置向两边扩展,记录并更新得到的最长的回文长度。这里要注意回文串的长度是计数还是偶数,因此需要分别进行讨论。
具体的Java代码如下,语句都很通用,读者可以很容易的转换为其他语言:
1 import java.util.*; 2 class Test { 3 public static int maxhuiwenlength(StringBuilder str){ 4 if(str.length()==0)return 0; //如果长度为空字符串,直接返回0 5 int i,j,max=0,p; //max记录最长的长度 6 int n=str.length(); //求字符串长度 7 for(i=0;i<n;i++) 8 { 9 for(j=0;(i-j)>=0 && (i+j)<n;j++) //假设长度为奇数的回文串 10 if(str.charAt(i-j)!=str.charAt(i+j))break; 11 p=(j-1)*2+1; 12 if(p>max)max=p; 13 for(j=0;(i-j)>=0 && (i+j+1)<n;j++) //假设长度为偶数的回文串 14 if(str.charAt(i-j)!=str.charAt(i+j+1))break; 15 p=(j-1)*2+2; 16 if(p>max)max=p; 17 } 18 return max; //返回回文串长度 19 } 20 } 21 22 public class Main { 23 public static void main(String[] args) { 24 StringBuilder str=new StringBuilder("123458"); 25 System.out.print("字符串:"+str+"的最大回文子串长度为:"+Test.maxhuiwenlength(str)); 26 } 27 28 }
问题描述2:给定一个字符串,求它的最长回文子串的长度,并且回文子串的字符在原字符串中可以不连续。也就是说可以删除原字符串中的一个或多个字符。
分析:该问题与问题1相比,有了一定的难度,难度在于可以删除原字符串中的字符,但是基本思想还是和问题1一样,需要在问题1上做一些改进。
同样需要分别考虑回文串是奇数还是偶数,无论哪种情况,都遵循这个过程。
当扫描到两端字符相等时,两端都要扩展,如果不相等,则要分两步进行:
第一步,固定右端,向左扫描一遍,如果发现相等,则两端同时扩展,长度加2.否则左指针-1
第二步,固定左端,向右扫描一遍,如果发现相等,则两端同时扩展,长度加2.否则右指针+1
具体的Java代码如下,代码写法比较通用,读者可以很容易转换为其他语言。
1 import java.util.*; 2 class Test { 3 public static int maxhuiwenlength(StringBuilder str){ 4 if(str.length()==0)return 0; //如果长度为空字符串,直接返回0 5 int i,j,max=0,p; //max记录最长的长度,p记录临时最长长度 6 int n=str.length(); //求字符串长度 7 int low,high; //两个指针分别指向两端 8 for(i=1;i<n;i++) 9 { 10 p=1; //寻找以i为中心的奇数长度的回文,最少长度也为1 11 low=i-1;high=i+1; 12 while(low>=0 && high<n) 13 { 14 if(str.charAt(low)==str.charAt(high)) //如果两端相等,两端同时扩展,长度加2 15 { 16 low--; 17 high++; 18 p=p+2; 19 } 20 else{ //如果两端不相等 21 int t=low; 22 while(--low>=0 && high<n) //右端固定不变,向左扫描一遍 23 if(str.charAt(low)==str.charAt(high)) //扫描到相等时,两边同时扩展,长度加2 24 { 25 high++; 26 p=p+2; 27 } 28 low=t; 29 while(low>=0 && ++high<n) //左端固定不变,向右扫描一遍 30 if(str.charAt(low)==str.charAt(high)) //扫描到相等时,两边同时扩展,长度加2 31 { 32 low--; 33 p=p+2; 34 } 35 } 36 } 37 if(p>max)max=p; //更新最长长度 38 39 p=0; //寻找以i-1,i为中心的偶数长度的回文 40 low=i-1;high=i; 41 while(low>=0 && high<n) 42 { 43 if(str.charAt(low)==str.charAt(high)) //如果两端相等,两端同时扩展,长度加2 44 { 45 low--; 46 high++; 47 p=p+2; 48 } 49 else{ 50 int t=low; 51 while(--low>=0 && high<n) //右端固定不变,向左扫描一遍 52 if(str.charAt(low)==str.charAt(high)) //扫描到相等时,两边同时扩展,长度加2 53 { 54 high++; 55 p=p+2; 56 } 57 low=t; 58 while(low>=0 && ++high<n) //左端固定不变,向右扫描一遍 59 if(str.charAt(low)==str.charAt(high)) //扫描到相等时,两边同时扩展,长度加2 60 { 61 low--; 62 p=p+2; 63 } 64 } 65 } 66 if(p>max)max=p; //更新最长长度 67 } 68 if(max==0)max=1; //因为只要字符串不为空,回文串最少也为1 69 return max; //返回回文串长度 70 } 71 } 72 73 public class Main { 74 public static void main(String[] args) { 75 StringBuilder str=new StringBuilder("123251"); 76 System.out.print("字符串:"+str+"的最大回文子串长度为:"+Test.maxhuiwenlength(str)); 77 } 78 79 }