5. 最长回文子串(Manachert算法)
Manachert算法:
求一个字符串串最长的回文子串
122131221
暴力法:每个字符都当作是中心字符,向两边扩,找到所有的
a121bcb121ckf
113
但是这外求法有一个问题如果回文是even是找不到的
122131221
1221就找不到了,因为没法找到虚轴
所以要做以下处理
#1#2#2#1#3
# 1 # 2 # 2 # 1 # 3 # 1 # 2 # 2 # 1 #
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
1 3 1 3 9 ........19
19/2=9
9/2=4
虚轴只是一个辅助作用,所以加什么特殊字符都可以,不会影响实轴
一些概念:
1:回文半径:从中心点从出发一侧的数量,直径:所有回文的数量
# 1 # 2 # 2 # 1 #
0 1 2 3 4 5 6 7 8
c
中心点c :回文半径是5,直径是9
2:最右回文边界R,初始值-1
# 1 # 2 # 2 # 1 #
c 0 1 2 3 4 5 6 7 8
r 1 3 4
3:中心点在哪?
当R更新到8时,是哪个中心点使它更新到8的?
只要R更新,C一定更新
通过以上的概念
把每个位置的中心位置的回文半径记一个数组里,叫回文半径
R=-1;
i=0,arr[0]=1;R=1
i=0,arr[1]=2;
i=0,arr[2]=2;
i=0,arr[3]=4;
i=0,arr[4]=8;
用这个来加速过程
R:最右回文右边界
arr[i]=? 以i为中心能扩多大
1:i在R外:暴力扩就行,无优化
2:i在R内:
L a【 i" 】b C k【 i 】f R
A B
a[i"]=R"
情况1:如果i"的回文区在LR内,a[i]=a[i"]
证明:
a!=b----->k!=f
a==k
b==f
情况2:i"的回文区在LR外,a[i]=R-i
证明:
( a【L i" ]b ) C k【 i R】f
a==b
b==k
a!=f
----->k!=f
情况3:i"的回文区和L边界重合,有一个至少不要验证的区域,从R+1继续验证下去
public static int maxLcpsLength(String str){ if(str==null||str.length()==0){ return 0; } char[] charArr=manacherString(str); int[] pArr=new int[charArr.length]; int C=-1;//中心点位置 //[L C R-1]R int R=-1;//R代表最右扩成功位置的下一个 int max=Integer.MIN_VALUE; for(int i=0;i!=charArr.length;i++){ //i位置扩出来的答案,i位置扩的区域,至少是多大 //R>i,表示i在R内,2*C-i就是i"的位置,则i的回文半径最少是i"的回文半径和R-i较少的那一个 //i在R外,i的回文半径最少是1 //表示i最少的回文半径,这个区域是不用验的,接着这个位置继续扩 pArr[i]=R>i?Math.min(pArr[2*C-i],R-i):1; //while内的条件代表,这个半径没有越界 while(i+pArr[i]<charArr.length&& i-pArr[i]>-1){ //( a【L i" ]b ) C k【 i R】f if(charArr[i+pArr[i]]==charArr[i-pArr[i]]){ //相等,则扩 pArr[i]++; }else{ break; } } //看有没有刷新R if(i+pArr[i]>R){ //R更新 R=i+pArr[i]; //中心点也更新 C=i; } //求最大值 max=Math.max(max,pArr[i]); } return max-1;// 最大回文串2max/2-1,示例回文半径是5的情况 }
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
class Solution { public String longestPalindrome(String s) { if(s.length()==1){ return s; } String str=getManacherStr(s); char[] chs=str.toCharArray(); int[] pArr=new int[chs.length]; int C=-1; int R=-1; int j=-1; int max=Integer.MIN_VALUE; for(int i=0;i<chs.length;i++){ pArr[i]=R>i?Math.min(pArr[2*C-i],R-i):1; while(i+pArr[i]<chs.length&& i-pArr[i]> -1){ if(chs[i+pArr[i]]==chs[i-pArr[i]]){ pArr[i]++; }else{ break; } } if(i+pArr[i]>R){//推动R R=i+pArr[i]; C=i;//推动R的中心点位置 } //收集最大回文的结果 if(pArr[i]>max){ max=pArr[i];//最大回文半径 j=i;//最大回文半径时的中心点 } } //j,max String bigStr=str.substring(j-max+1,j+max); if(bigStr==""){ return s.substring(0,1); }else{ return removeManacherStr(bigStr); } } public String removeManacherStr(String str){ String res=""; if(str.length()==1){ if(str.equals("#")){ return res; }else{ return str; } } for(int i=0;i<str.length();i++){ if(str.charAt(i)!='#'){ res+=str.charAt(i); } } return res; } public String getManacherStr(String str){ String res="#"; for(int i=0;i<str.length();i++){ res+=str.charAt(i)+"#"; } return res; } }