最长回文子串(longest Palindrome)
①longest common substring
将原字符串str1整个转置(reverse)得到str2,对str2的所有子字符串substr2,检查str1是否包含substr2,如果包含,这里要特别注意检查,substr2是否是一个回文串,例如str1=“bacac”,则str2=“cacab”,此时substr2=“cac”,正确,但是若str1=“eeecdfeee”,则str2="eeefdceee",此时substr2=“eee”,错误,所以对于substr2是否是回文串的检查是非常必要的。
要点:利用动态规划的方法找出原字符串和它转置后得到的字符串的“最长公共字符串”,同时要保证该“最长公共字符串”是一个回文串
以下是我的具体实现,leetcode上面说时间复杂度为O(n^2,然后我写了一下,一直是超时错误,不知道从哪里改进了〒_〒)
public String longestPalindrome(String s) {
StringBuffer str1 = new StringBuffer();
//整个转置之后得到的字符串
StringBuffer str2 = new StringBuffer(s);
str2.reverse();
int[][] c = new int[2][s.length()+1];
int maxI=0;//当前最长公共子字符串的位置
int length=0;//最长公共子字符串的长度
for(int i = 1; i < s.length()+1; i++)
{
for(int j = 1; j < str2.length()+1; j++)
{
if(s.charAt(i-1)==str2.charAt(j-1))
c[1][j]=c[0][j-1]+1;
else
c[1][j]=0;
if(c[1][j]>length){
if(s.substring(i-c[1][j],i).equals(str2.substring(s.length()-i,s.length()-i+c[1][j])))
{
length=c[1][j];
maxI=i;
}
}
}
for(int k=0;k<str2.length()+1;k++)
{
c[0][k]=c[1][k];
c[1][k]=0;
}
}
return s.substring(maxI-length, maxI).toString();
②暴力破解法(穷举所有的子串,并判断其是否是回文串),时间复杂度O(n^3),超时
③动态规划法,时间复杂度O(n^2)
int length=s.length();
int maxlength=1;
int start =0;
boolean[][]P = new boolean[s.length()][s.length()];
for(int i=0;i<length;i++)//初始化准备
{
P[i][i]=true; //对称点为一个字母
if(i<length-1&&s.charAt(i)==s.charAt(i+1))
{ //对称点为两个字母
P[i][i+1]=true;
start=i;
maxlength=2;
}
}
for(int len=3;len<=length;len++)//子串长度
for(int i=0;i<=length-len;i++)//子串起始地址
{
int j=i+len-1;//子串结束地址
if(P[i+1][j-1]&&s.charAt(i)==s.charAt(j))
{
P[i][j]=true;
maxlength=len;
start=i;
}
}
return s.substring(start,start+maxlength);
④中部扩展法(leetcode给出的简洁明了的代码)
public String longestPalindrome(String s) { int start = 0, end = 0; for (int i = 0; i < s.length(); i++) { int len1 = expandAroundCenter(s, i, i);//对称点为一个字母 int len2 = expandAroundCenter(s, i, i + 1);//对称点为两个字母 int len = Math.max(len1, len2); if (len > end - start) { start = i - (len - 1) / 2; end = i + len / 2; } } return s.substring(start, end + 1); } private int expandAroundCenter(String s, int left, int right) { int L = left, R = right; while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) { L--; R++; } return R - L - 1; }
⑤Manacher's Algorithm
这个算法最大的亮点就是在每一个字符之间手动加入了一个#字符,包括字符串的头部和尾部。时间复杂度为O(n)
例如:str1="aba",预处理之后就变成了=》“#a#b#a#”,这样处理之后新的str1就只能是关于一个字母对称的字符串,不用考虑关于两个字母中心对称的情况,接下来要做的和④中部扩展法的大体相似,不过只需要考虑对称点为一个字母的情况
public String longestPalindrome(String s) { int start = 0, end = 0; for (int i = 0; i < s.length(); i++) { int len = expandAroundCenter(s, i, i);//对称点为一个字母 if (len > end - start) { start = i - (len - 1) / 2; end = i + len / 2; } } return s.substring(start, end + 1); } private int expandAroundCenter(String s, int left, int right) { int L = left, R = right; while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) { L--; R++; } return R - L - 1; }