回文字符串:
- 正着看反着看是一样的
- abccba abcba 存在一个轴对称
最长回文字符串问题
在一个字符串中找到最长回文字符串,而manacher算法就是去找这个最长回文字符串。
有啥用?
DNA序列 回文基因序列有一些生理学意义
暴力解最长回文问题( O ( N 2 ) O(N^2) O(N2))
ab|ab 按照虚轴对称
ababa 按照实轴中间的a对称
遍历每个实轴与虚轴,分别向两边扩展,存最长的
public static String getmaxstr(String s)
{
int maxlength=0;
String maxstr="";
//从每一个轴扩展 包含实轴(是一个数,找奇数最长回文字符串) 和虚轴(找偶数最长回文字符串)
for(int i=0;i<s.length();i++)
{
//以该字母为实轴
int m=i;
int n=i;
while(m>=0&&n<s.length()&&s.charAt(m)==s.charAt(n))
{
if(n-m+1>maxlength)
{
maxlength=n-m+1;
maxstr=s.substring(m, n+1);
}
m-=1;
n+=1;
}
//以该字母后面为虚轴
m=i;
n=i+1;
while(m>=0&&n<s.length()&&s.charAt(m)==s.charAt(n))
{
if(n-m+1>maxlength)
{
#### #### maxlength=n-m+1;
maxstr=s.substring(m, n+1);
}
m-=1;
n+=1;
}
}
return maxstr;
}
Manacher算法O(N)
其存储一定信息,加速上述思路的扩展过程,存取必要信息,达到线性时间(与kmp类似哈)
加速tips的理解:
存储的信息
- 回文半径数组(以每一位为中心的回文字符串长度)
- r(最远的右边界)
- c(最远的右边界的中心点)
几种情况
- i在r外的时候 没有任何优化
- i在r内的时候 若镜面无法完全包含,我们直接跳到镜面外的字母开始更新就行
- i在r内的时候 若镜面完全包含,我们没必要拓展这个字母
代码
//tested
//abcd->#a#b#c#d#
public static char[] tostrx(String str)
{
char[] strx=new char[2*str.length()+1];
int l=0;
for(int i=0;i<strx.length;i++)
{
if(i%2==0)
{
strx[i]='#';
}
else {
strx[i]=str.charAt(l);
l+=1;
}
}
return strx;
}
//tested
//检测i是否在数组x(长度为l)越界
public static boolean isvalid(int i,int l)
{
if(i>=0&&i<l)
return true;
return false;
}
//tested
//返回该节点的最大回文字符串长度
public static int expand(char[] strx,int i,int j)//以i节点为中心,从j节点开始扩展
{
while(isvalid(j, strx.length)&&isvalid(2*i-j,strx.length)&&strx[j]==strx[2*i-j])
{
j+=1;
}
j-=1;
return 2*(j-i)+1;
}
//tested
//得到最大回文字符串(去掉#)
public static String str(char[] t,int[] parax)
{
int maxi=-1;
int max=-1;
for(int i=0;i<parax.length;i++)
{
if(parax[i]>max&&!(i%2==0&¶x[i]==1))
{
max=parax[i];
maxi=i;
}
}
String str=tostr(t, maxi-(max-1)/2, maxi+(max-1)/2);
return str;
}
//manacher算法
public static String manacher(String str)
{
char[] strx=tostrx(str);
int[] parax=new int[strx.length];//存取每一位的回文字符串的最大长度
int c=-1;
int r=-1;//存取最远右边界
for(int i=0;i<strx.length;i++)
{
if(i<r)
{
//parax[2*c-i]-1)/2+i 由镜像节点得到当前节点的右边界
if((parax[2*c-i]-1)/2+i<r)//在镜面内
{
parax[i]=parax[2*c-i];
}
else {
parax[i]=expand(strx, i,r);
}
}
else {
parax[i]=expand(strx, i, i+1);
}
}
String string=str(strx,parax);
return string;
}
//tested
//给定#a#b#c#d# 第几位到第几位 截取并删除#
public static String tostr(char[] strx,int a,int b)
{
String str="";
for(int i=a;i<=b;i++)
{
if(i%2!=0)
{
str=str+strx[i];
}
}
return str;
}