0
点赞
收藏
分享

微信扫一扫

力扣(Leetcode):最长回文串

慕容冲_a4b8 2022-01-16 阅读 23

最长回文串


题目:
给你一个字符串 s,找到 s 中最长的回文子串。
Input:
“babad”
Output:
“aba"或者"bab”

暴力解法

不能AC解法很容易就想到两重循环,提取所有可能的字符串,然后判断是否是回文串

class Solution {
    public String longestPalindrome(String s) {
        int len = 0;//比较值
        String last = "";//输出值
        for(int i = 0;i < s.length();i++)
        {
            for(int j = i;j <= s.length();j++)
            {
                String b;
                b = s.substring(i,j);//提取字符串
                StringBuffer c = new StringBuffer(b);
                if(c.reverse().toString().equals(b))//判断是否为回文数
                {
                    if(b.length()>len)
                    {
                        len = b.length();
                        last = b;
                    }
                }
            }
        }
        return last;
    }
}

很遗憾超时。reserve的时间复杂度O(n)
时间复杂度:O(n^3)
在这里插入图片描述

最长公共子串

用二维数组,例如babad,转置dabab,注意aba转置后没有变,所以定义一二维数组,起始排相等定义1,以后再相似便加一,找到最长的相等字符串,并定位其下标。

-babad
d00001
a01010
b10200
a02030
b10300

应该不能AC解法:找到最长公共子串长度,既3,所以推出是aba和bab

public String longestPalindrome(String s) {
 		if (s.equals(""))
 				return "";
 			String origin = s;
 			String reverse = new StringBuffer(s).reverse().toString(); //字符串倒置
 			int length = s.length();
 			int[][] arr = new int[length][length];
			 int maxLen = 0;//记录长度
			 int maxEnd = 0;//记录下标
 		for (int i = 0; i < length; i++)
 			for (int j = 0; j < length; j++) {
 					if (origin.charAt(i) == reverse.charAt(j)) {
 						if (i == 0 || j == 0) {
 							arr[i][j] = 1;
						 } else {
 							arr[i][j] = arr[i - 1][j - 1] + 1;
 								}
 					}
			 if (arr[i][j] > maxLen) {
 					maxLen = arr[i][j];
 					maxEnd = i; //以 i 位置结尾的字符
 				}
 		}
 }
		 return s.substring(maxEnd - maxLen + 1, maxEnd + 1);
}	

为什么说不能AC呢?例如字符串“ab51ba”,把二维数组打出来

-ab51ba
a100001
b020010
1000100
5001000
b010010
a100002

可以找到ab和ba是最长公共子串,但是都不是回文串,注意观察,我们注意到第一个2所定位的b,它在未倒装之前,在字符串的位置并不是列标所对应的1,而是后面的4,所以并不是同一个字符。那我们只用加一个判断,判断最后一个字符,再更新上面代码Maxlen和Maxend。字符串长度 — 行标 —1 + Maxlen — 1 = 列标?
加上代码块
int beforeRev = length - 1 - j; if (beforeRev + arr[i][j] - 1 == i) //判断下标是否对应
对于空间的优化: 用二维数组不难发现,其实都是上一行来推出下一行,所有我们可以用一维数组来进行空间的优化

class Solution {
    public String longestPalindrome(String s) {
            if (s.equals(""))
                return "";
            String origin = s;
            String reverse = new StringBuffer(s).reverse().toString();
            int length = s.length();
            int[] arr = new int[length];
            int maxLen = 0;
            int maxEnd = 0;
            for (int i = 0; i < length; i++)
            /**************修改的地⽅***************************/
                for (int j = length - 1; j >= 0; j--) {
                    /**************************************************/
                    if (origin.charAt(i) == reverse.charAt(j)) {
                        if (i == 0 || j == 0) {
                            arr[j] = 1;
                        } else {
                            arr[j] = arr[j - 1] + 1;
                        }
                         /**************修改的地⽅***************************/
                        //之前⼆维数组,每次⽤的是不同的列,所以不⽤置 0 。
                    } else {
                        arr[j] = 0;
                    }
                    /**************************************************/
                    if (arr[j] > maxLen) {
                        int beforeRev = length - 1 - j;
                        if (beforeRev + arr[j] - 1 == i) {
                            maxLen = arr[j];
                            maxEnd = i;
                        }
                    }
                }
        return s.substring(maxEnd - maxLen + 1, maxEnd + 1);
    }
}

时间复杂度:O(n^2) | 空间复杂度:O(n)
在这里插入图片描述

中心扩展

偷偷地盗了张图,由图看,我们可以找一个中心,然后双指针左右移动,如果指向字符相等的话,就说明是回文的,有n个数,数为中心和两数中间为中心,共有n+n-1个中心。
例如a所在中心是2,向左右扩展,得到ababa,得到出不包括中心的最长长度为4然后根据中心来推出起始和终末。
在这里插入图片描述

class Solution {
    public String longestPalindrome(String s) {
                if(s == null)
                    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);//找出以i为中心的最长回文串的长度,不包括中心点
                    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 L,int R){
       while(L >= 0&&R < s.length() && s.charAt(L) == s.charAt(R))
       {
               L--;
               R++;
       }
       return R - L - 1;
   }
}

时间复杂度:O(n^2) | 空间复杂度:O(1)

Manacher’s Algorithm 马拉车算法。

1975年由Manacher前辈发明。
链接: link1.
链接: link2.
根据中心拓展法,中心有两种,奇数,偶数,但是我们在字符串中插入#
如ababd,插入字符#,并且用^KaTeX parse error: Expected group after '^' at position 11: 来控制始末。得到" ^̲#a#b#a#b#d#"保证字符串的个数永远是奇数。然后想要一个辅助数组P[i],存入,以i为中心的拓展长度,找到最大的数,便是最长的回文串长度,然后根据中心i推出start,end。向之前一样,输出。
例如6c的p[i]是5,就是向左和向右拓展为五,而去掉#,最长回文串刚好是五
在这里插入图片描述
当然最主要的还是求p[i]:
方法一当然是以每一个字符为中心,然后用中心拓展求出辅助数组p[i],应该是求得出来的
方法二,我看了半天,后面从别的地方了解到,是根据前一部分求出的数组来更新
在这里插入图片描述
例如求?处的p[i]它的初始值可以是镜像对称的值,但是可能它的镜像p[i_mirror]是大于往右拓展的值的,例如p[i_mirror] = 4,但是i不能再向右拓展4,所以用两者最小的为初始值,然后再根据这个中心拓展,是不是能够找到更长的回文串while (T.charAt(i + 1 + P[i]) == T.charAt(i - 1 - P[i])) { P[i]++; }
然后再更新C和R。
剩下的不多说了吧,找到最大值,然后定位start,end,截取字符串,输出。

import java.util.*;
class Solution {
    public String preProcess(String s) {
        int n = s.length();
        if (n == 0) {
            return "^$";
        }
        String ret = "^";
        for (int i = 0; i < n; i++)
            ret += "#" + s.charAt(i);
        ret += "#$";
        return ret; }
    // ⻢拉⻋算法
    public String longestPalindrome(String s) {
        String T = preProcess(s);
        int n = T.length();
        int[] P = new int[n];
        int C = 0, R = 0;
        for (int i = 1; i < n - 1; i++) {
            int i_mirror = 2 * C - i;
            if (R > i) {
                P[i] = Math.min(R - i, P[i_mirror]);// 防⽌超出 R
            } else {
                P[i] = 0;// 等于 R 的情况
            }
            // 碰到之前讲的三种情况时候,需要利⽤中⼼扩展法
            while (T.charAt(i + 1 + P[i]) == T.charAt(i - 1 - P[i])) {
                P[i]++;
            }
            // 判断是否需要更新 R
            if (i + P[i] > R) {
                C = i;
                R = i + P[i];
            }
        }
        // 找出 P 的最⼤值
        int maxLen = 0;
        int centerIndex = 0;
        for (int i = 1; i < n - 1; i++) {
            if (P[i] > maxLen) {
                maxLen = P[i];
                centerIndex = i;
            }
        }
        int start = (centerIndex - maxLen) / 2; //最开始讲的求原字符串下标
        return s.substring(start, start + maxLen);
    }
}

以上是看了沉默王二的题解和部分力扣上的题解所写,算是笔记本好了

举报

相关推荐

0 条评论