题目: Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
Example 2:
Input: “cbbd”
Output: “bb”
翻译:
给定一个字符串s,找出s中最长的回文子串。可以假设s的最大长度为1000。
示例1:
输入:“babad”
输出:“巴布”
注:“aba”也是一个有效的答案。
示例2:
输入:“cbbd”
输出:“bb”
什么是回文串:
子串:小于等于原字符串长度由原字符串中任意个连续字符组成的子序列
回文:关于中间字符对称的文法,即“aba”(单核)、“cabbac”(双核)等
最长回文子串:
- 寻找回文子串;
- 该子串是回文子串中长度最长的。
方法一: 移动滑块从外向内找回文串:先找最长的,下来一次递减;
时间复杂度O(n3) ;
代码实现:
class Solution {
public boolean Palindrome(String s,int left,int right){
while(left<right){
if(s.charAt(left++)!=s.charAt(right--))
return false;
}
return true;
}
public String longestPalindrome(String s) {
int len=s.length();
if(s==null || len < 2)
return s;
int len1=len;
//从长到短判断回文子串
while(len1>0){
for(int i=0 ;i<=len-len1 ; i++){
if(Palindrome(s,i,i+len1-1))
return s.substring(i,i+len1);
}
len1--;
}
return "";
}
}
方法二:动态规划,从中心向外进行扩散;
时间复杂度: O(n2);
思路:我们将s中任何一点(奇数回文串是一点用i表示,偶数是两点用i和i+1表示)作为回文串的中点,然后设置left和right表示其中点左右坐标;那么当left和right的元素相同时,回文串的长度加2,此时与已知的最长回文子串进行比较,找到最长的回文串;
class Solution {
int maxLen=0; //最长回文串长度
int start=0;//回文串开始角标
int len=0;
public void Palindrome(String s,int left,int right){
while(left>=0&&right<len&&s.charAt(left--)==s.charAt(right++));
if(maxLen<right-left-1){ //判断当前子串和最长子串的长度
start=left+1;
maxLen=right-left-1;
}
}
public String longestPalindrome(String s) {
len=s.length();
if(s==null || len < 2) //如果只有一个元素或者没有元素
return s;
for(int i=0 ; i<len-1 ; i++){
Palindrome(s,i,i);//奇数
Palindrome(s,i,i+1);//偶数
}
return s.substring(start,start+maxLen);
}
}
方法三:Manacher算法,算法思想马拉车算法详解,请点击
时间复杂度:O(n):
步骤:
- 用字符串中没有的元素(一般可以选#)将每个元素分隔开来,并存出集合中;
- 构建一个包含分割元素长度的数组,值为以数组角标为中心的的回文串的一半长度;
- 遍历集合时,每次找到左边与j相对于最右回文串的角标;
- 对角标的值进行判断,如果角标代表的回文串完全包含在最右回文串里面,那么j角标对应的值与左边的值一样;
- 如果只是部分包含,那么只需要判断未包含部分什么时候会形成以j为中心的最长回文串;
- 如果j都没有在最右回文串里面,那么以j为中心,左右遍历找到回文串;
代码实现:
class Solution {
public int Palindrome(List<Character> list, int mid,int l,int r){
//匹配得到回文串
while(l>=0&&r<list.size()&&list.get(l).equals(list.get(r))){
//集合里面是包装类,不要用==
l--;
r++;
}
if(r==mid+1)
return 0;
return r-mid-1;//中点右边元素个数
}
public String longestPalindrome(String s) {
int len=s.length();
List<Character> list=new ArrayList(len*2+1);
for(int i=0 ;i<len ;i++){
list.add('#');
list.add(s.charAt(i));
}
list.add('#');
int[] ln=new int [len*2+1];
int r=-1; //最右回文串右端角标
int mid=-1;//最右回文串中点坐标
int maxMid=-1;//最长回文串中间坐标
int maxLen=-1;//最长回文串一端长度;
for(int j=0 ; j <len*2+1 ; j++){
if(j<r){
int leftLen=ln[2*mid-j];
if(leftLen< r-j){
//对称左边回文串长度没有超出以r为右端点的回文串范围
ln[j]=leftLen;
}else{
//对称左边回文串长度超出范围不知道匹配不匹配,对超出部分进行匹配校验
ln[j]=Palindrome(list,j,j-(r-j),j+r-j);
}
}else{
//完全没有左边堆成回文串需要无脑匹配
ln[j]=Palindrome(list,j,j-1,j+1);//一半长度;
}
if(j+ln[j]>r){ //当前回文串右端与保存的右端取大
r=j+ln[j];
mid=j;
}
if(maxLen<ln[j]){ //得到目前最长回文子串
maxLen=ln[j];
maxMid=j;
}
}
StringBuilder sb=new StringBuilder();
for(int z=maxMid-maxLen ; z<= maxMid+maxLen ;z++){
//去除#,得到最长回文子串
char ch=list.get(z).charValue();
if(ch!='#')
sb.append(ch);
}
return sb.toString();
}
}