1.题目
2.方法一:暴力
时间复杂度O(N的三次方):这里 N 是字符串的长度,枚举字符串的左边界、右边界,然后继续验证子串是否是回文子串,这三种操作都与N相关;
空间复杂度:O(1),只使用到常数个临时变量,与字符串长度无关。
3.方法二:动态规划
3.1 分析
初始状态dp[i][i]=true。
如果c[i]!=c[j],返回false;
如果c[i]==c[j],要看i和j之间字串是不是回文串,如果字串也是,dp[i][j]=true。(转移方程dp[i][j]=dp[i+1][j-1])
边界情况:[i+1][j-1]长度小于等于1(即j-i<3),dp[i][j]必定为true。
保存结果:起始下标beginIndex和回文串长度maxLength。这样可以省去每次截取字串消耗的时间。等返回时再截取最终结果。
3.2 代码
class Solution {
public String longestPalindrome(String s) {
int len=s.length();
if(len<2)
return s;
char[] charArray=s.toCharArray();
boolean[][] dp=new boolean[len][len];
int maxLength=1;//最长回文串的长度
int begin=0;//最长回文串的起始下标
//初始化
for(int i=0;i<len;i++){
dp[i][i]=true;
}
for(int j=1;j<len;j++){//枚举右边界
for(int i=0;i<j;i++){//枚举左边界
//字符不相等
if(charArray[i]!=charArray[j]){
dp[i][j]=false;
continue;
}
if(j-i+1<=3){//字串长度小于等于3,即中间只有一个数,必定可以
dp[i][j]=true;
}
else{
dp[i][j]=dp[i+1][j-1];
}
//更新最大值
if((j-i+1)>maxLength&&dp[i][j]){
maxLength=j-i+1;
begin=i;
}
}
}
return s.substring(begin,begin+maxLength);
}
}
3.3 复杂度
时间复杂度O(N²)
空间复杂度O(N²)
3.4 结果
4.方法三:中心扩散法
4.1 思路
边界情况即为子串长度为 1或 2的情况。我们枚举每一种边界情况,并从对应的子串开始不断地向两边扩展。如果两边的字母相同,我们就可以继续扩展,例如从 P(i+1,j-1) 扩展到 P(i,j);如果两边的字母不同,我们就可以停止扩展,因为在这之后的子串都不能是回文串了。
4.2 代码
class Solution {
public String longestPalindrome(String s) {
int len=s.length();
if(len<2)
return s;
int maxLen=1;
String maxStr=s.substring(0,1);//最短结果是第一个字符
//将中心位置枚举到len-2即可
for(int i=0;i<=len-2;i++){
String a=find(s,i,i);//得到奇数长度
String b=find(s,i,i+1);//得到偶数长度
String str=a.length()>b.length()?a:b;
if(maxLen<str.length()){
maxLen=str.length();
maxStr=str;
}
}
return maxStr;
}
public String find(String s,int i,int j){
int left=i;
int right=j;
int len=s.length();
while(left>=0&&right<len&&s.charAt(left)==s.charAt(right)){
left--;
right++;
}
return s.substring(left+1,right);//因为最后[left]!=[right],所以left和right都不能取
}
}
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) {
return "";
}
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);
}
public int expandAroundCenter(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
--left;
++right;
}
return right - left - 1;
}
}
复杂度
5. 方法四:马拉车
算法介绍
https://www.cxyxiaowu.com/2665.html
https://zhuanlan.zhihu.com/p/70532099
代码
class Solution {
public String longestPalindrome(String s) {
StringBuilder t=new StringBuilder();
t.append("$#");
for(int i=0;i<s.length();i++){
t.append(s.charAt(i));
t.append("#");
}
t.append("!");
int imax=0,rmax=0,ans=0;
int n=t.length();
int[] f=new int[n];//以i为中心的字串数
int start=1,end=0;//初始长度为0
for(int i=1;i<n;i++){
f[i]=(i<=rmax?Math.min(f[2*imax-i],rmax-i+1):1);
while(i+f[i]<n&&i-f[i]>=0&&t.charAt(i+f[i])==t.charAt(i-f[i])){
f[i]++;
}
//当前半径大于最长半径
//更新中心点和最右点
if(i+f[i]-1>rmax){
imax=i;
rmax=i+f[i]-1;
}
//当前长度大于最大长度,更新最大长度的起始点
if(((rmax-imax)*2+1)>(end-start+1)){
start=imax-(rmax-imax);
end=rmax;
}
}
StringBuilder res=new StringBuilder();
for(int i=start;i<=end;i++){
char c=t.charAt(i);
if(c!='#')
res.append(c);
}
return res.toString();
}
}
复杂度
时间O(n):由于 Manacher 算法只有在遇到还未匹配的位置时才进行匹配,已经匹配过的位置不再匹配,因此对于字符串 S 的每一个位置,都只进行一次匹配,算法的复杂度为 O(N) 。
空间O(n)