原文地址java
给定一个字符串s,找出其中最长的回文格式的子字符串。你能够假设长度的最大值为1000.git
Example:github
Input: "babad"
Output: "bab"
复制代码
Note: "aba" is also a valid answer.算法
Example:c#
Input: "cbbd"
Output: "bb"
复制代码
一开始觉得palindrome是重复的意思,走了很大的弯路,后来才知道指的是回文格式,就是一个顺着读和反过来读都同样的字符串。 由此可知他有两种状况,一种是奇数的状况,中间的一个字符独立,其他的字符以中间为轴两两对应。另外一种是偶数的状况,全部的字符都以中间为轴两两对应。数组
public class Solution {
private int lo, maxLen;
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2)
return s;
for (int i = 0; i < len - 1; i++) {
extendPalindrome(s, i, i); //assume odd length, try to extend Palindrome as possible
extendPalindrome(s, i, i + 1); //assume even length.
}
return s.substring(lo, lo + maxLen);
}
private void extendPalindrome(String s, int j, int k) {
while (j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k)) {
j--;
k++;
}
if (maxLen < k - j - 1) {
lo = j + 1;
maxLen = k - j - 1;
}
}
}
复制代码
分析 这个方法分为奇偶两种状况进行计算。首先对每个字符串里的字符进行两种状况的计算,extendPalindrome方法的原理就是当对应的位置的字符相同时,就将左侧字符向左一位,右侧字符向右一位,而后再重复进行这个比较过程。 时间复杂度 : O(n^2) 。n是字符串的长度 空间复杂度 : O(n) .bash
原文地址微信
public class Solution {
public String longestPalindrome(String s) {
String res = "";
int currLength = 0;
for(int i=0;i<s.length();i++){
if(isPalindrome(s,i-currLength-1,i)){
res = s.substring(i-currLength-1,i+1);
currLength = currLength+2;
}
else if(isPalindrome(s,i-currLength,i)){
res = s.substring(i-currLength,i+1);
currLength = currLength+1;
}
}
return res;
}
public boolean isPalindrome(String s, int begin, int end){
if(begin<0) return false;
while(begin<end){
if(s.charAt(begin++)!=s.charAt(end--)) return false;
}
return true;
}
}
复制代码
分析 这个方法主要是每当指针向右移时,咱们都以这个位置的字符为结尾看是否能有新的长度为length(current length +1 或者 current length +2)的回文字符串。spa
时间复杂度 : O(n^2) 。n是字符串长度。 空间复杂度 : O(n) .3d
public class Solution {
public String longestPalindrome(String s) {
char[] str = changeString(s);
String result = manacher(str,s);
return result;
}
/** * 返回例如 #a#c#b#c#a#a#c#b#c#d#形式的字符串数组 * * @param s * @return */
public static char[] changeString(String s)
{
char[] str = new char[s.length() * 2 + 1];
int i = 0;
for (; i < s.length(); i++)
{
str[2 * i] = '#';
str[2 * i + 1] = s.charAt(i);
}
str[2 * i] = '#';
return str;
}
/** * manacher 算法实现找到回文字符串最长的一个 */
public static String manacher(char[] s,String olds) {
String result = "";
int rad[] = new int[s.length];
int start = 0;
int end = 0;
//i index,j 回文半径,k
int i = 1, j = 0, k;
// 记录最长的回文串的长度
int maxLen = 0;
while (i < s.length)
{
// 扫描得出rad值
while (i - j - 1 > -1 && i + j + 1 < s.length
&& s[i - j - 1] == s[i + j + 1]) {
j++;
}
if (maxLen < j ) {
maxLen =j;
start = i - j ;
end =i + j ;
}
rad[i] = j;
maxLen = maxLen > j ? maxLen : j;
k = 1;
//当回文中包含子回文,看子回文是否超出父回文边界。 分三种状况。
while (k <= rad[i] && rad[i - k] != rad[i] - k)
{
rad[i + k] = Math.min(rad[i - k], rad[i] - k);
k++;
}
i = i + k;
j = Math.max(j - k, 0);
}
result = olds.substring(start/2,end/2);
return result;
}
}
复制代码
分析 在这个问题中,回文的状况一共有两种,一种是奇数回文,一种是偶数回文,为了将他们合并成一种状况,咱们能够在首尾和每两个字符中间加上一个特殊字符,如‘#’,形如"#a#b#b#c#a#".这样咱们就将全部的回文状况合并成奇数回文的状况。
在字符串s中,咱们用rad[i]表示第i个字符的回文半径,能够获得s[i-rad[i],i-1] = s[i+1,i+rad[i]],只要求出了全部的rad,就求出了全部奇数长度的回文子串。
当咱们获得了rad[1..i-1]的值,并经过比较对称字符获得当前字符i的rad值至少为j,求出了rad[i]。如今咱们设一个指针k,从1循环到rad[i],以此来求出[i+1,i+rad[i]]的rad值。
根据定义,黑色的部分是一个回文子串,两段红色的区间全等。 由于以前已经求出了rad[i-k],因此直接用它.有3种状况: (1)rad[i]-k < rad[i-k]
如图,rad[i-k]的范围为墨绿色。由于黑色的部分是回文的,且墨绿色的部分超过了黑色的部分,因此rad[i+k]至少为rad[i]-k,即橙色的部分。有没有可能rad[i+k]的值大于rad[i]-k呢?这是不可能发生的,根据回文的特性咱们知道,若是橙色部分之外也是回文,那么黑色的回文部分就能够向外拓展。与假设冲突。所以rad[i+k] = rad[i]-k。
(2)rad[i]-k > rad[i-k]
如图,rad[i-k]的范围为墨绿色。由于黑色的部分是回文的,且墨绿色的部分在黑色的部分里面,根据回文特性,很容易得出:rad[i+k] = rad[i-k]。
根据上面两种状况,能够得出结论:当rad[i]-k != rad[i-k]的时候,rad[i+k] = min(rad[i]-k,rad[i-k])。
(3)rad[i]-k = rad[i-k]
如图,经过和第一种状况对比以后会发现,由于墨绿色的部分没有超出黑色的部分,因此即便橙色的部分全等,也没法像第一种状况同样引出矛盾,所以橙色的部分是有可能全等的。可是,根据已知的信息,咱们不知道橙色的部分是多长,所以就把i指针移到i+k的位置,j=rad[i-k](由于它的rad值至少为rad[i-k]),是否能够向外拓展等下个循环再作。
时间复杂度 : O(n) 。看起来程序里用到了循环嵌套,可是实际上他只对没有计量过的i进行计量。 空间复杂度 : O(n) .
若是你有更好的办法或者对我这里的描述有其余见解,请联系我。谢谢 原文地址
个人博客 leonchen1024.com
个人 GitHub github.com/LeonChen102…
微信公众号