一、前言
回文串定义:像abccba、aba这样的左右镜像对称的字符串称为回文串
子串定义:字符串abc中的子串有:a、b、c、ab、bc、abc
六个。长度为n的字符串中有C(n,1)+C(n-1,1)+…+1=n+n-1+…+1=n(n+1)/2,公式推算过程如下:
子串长度为1时,子串尾元素可放置的格子为第1~第n个格子共n个;
子串长度为2时,子串尾元素可放置的格子为第2~第n个格子共n-1个;
子串长度为3时,子串尾元素可放置的格子为第3~第n个格子共n-2个;
…
子串长度为n时,子串尾元素可放置的格子为第n个格子共1个;
序列串定义: 字符串abc 按顺序挑选字符组成的字符串,如 a、ab、ac、abc、b、bc、c共七个。按照组合语义就是从长度为n的字符串中随机抽出1~n个字符,那么每个元素要么被选中,要么没被选中,类比于n个二进制字符,共有2的n次方种可能,即长度为n的字符串序列串的个数为:
C(n,1)+C(n,2)+C(n,3)+…+C(n,n) = 2的n次方
随机组合串定义:从长度为n的字符串中随机选出x个字符(x>=1且x<=n)随机排列形成的字符串,如源串为abc,则a,b,c,ab,ac,ba,bc,ca,cb,abc,acb,bac,bca,cab,cba共15个,计算公式为A(n,1)+A(n,2)+…+A(n,n)=[e*n!-1]
https://zhuanlan.zhihu.com/p/636983899
二、算法思路
方法一:暴力求解
方法二:遍历基准点
基准点定义:字符串对称点,长度为奇数的字符串的基准点是正中间的字符,偶数的则为正中间的对称轴
方法三:动态规划
长度为m的字符串若是回文串则需满足1~m-2子串为回文串,且charAt(0)==charAt(m-1)
方法四:Manacher算法
此方法不在考察范围内
三、算法Java实现
方法一:暴力求解 时间复杂度:O(n^3)
public class Solution {
/**
* @param s: A string.
* @return: the longest Palindromic Substring in s.
*/
public String longestPalindrome(String s) {
if(s == null){
return "";
}
//外层循环控制子串长度,倒序循环
for(int length = s.length(), end = s.length() -1; length>0; --length) {
//内层循环控制子串窗口向左移动
for(int left = end-length+1; left >= 0; --left ) {
int right = left + length-1;
//判断子串是否为回文串
if(isPalindrome(s, left, right)) {
return s.substring(left, right+1);
}
}
}
return "";
}
private boolean isPalindrome(String s, int left, int right) {
//左右指针向中间靠拢,相向双指针
while(right >= left) {
//左右字符不相等就返回false
if(s.charAt(left) != s.charAt(right)) {
return false;
}
--right;
++left;
}
return true;
}
}
方法二: 遍历基准点,时间复杂度:O(n^2)
public class Solution {
/**
* @param s: A string.
* @return: the longest Palindromic Substring in s.
*/
public String longestPalindrome(String s) {
if(s == null){
return "";
}
int longest = 0;
String longestPalindrome = "";
//遍历的是字符串中的字符,即奇数子串的基准点
for(int oddBasePoint = 0; oddBasePoint < s.length(); ++oddBasePoint) {
//获取奇数子串基准点回文串
String odd = getPalindrome(s, oddBasePoint, oddBasePoint);
if(longest < odd.length()){
longest = odd.length();
longestPalindrome = odd;
}
//获取偶数子串基准点回文串
if(oddBasePoint != s.length() - 1){
String even = getPalindrome(s, oddBasePoint, oddBasePoint+1);
if(longest < even.length()){
longest = even.length();
longestPalindrome = even;
}
}
}
return longestPalindrome;
}
/*
*@param s 输入串
*@param left 基准点左移指针
*@param right 基准点右移指针
*/
private String getPalindrome(String s, int left, int right) {
//以基准点为中心,背向双指针
while(right <= s.length()-1 && left >= 0) {
if(s.charAt(left) != s.charAt(right)) {
break;//注意别写成return "";
}
++right;
--left;
}
return s.substring(left+1, right);
}
}
方法三:动态规划,设置一个二维数组boolean isPalindrome[s.length][s.length],isPalindrome[i][j]表示 i到j的子串是否为回文串,时间复杂度: O(n^2)
public class Solution {
/**
* @param s: A string.
* @return: the longest Palindromic Substring in s.
*/
public String longestPalindrome(String s) {
if(s == null){
return "";
}
int longest = 1;
String longestPalindrome = String.valueOf(s.charAt(0));
boolean[][] isPalindrome = new boolean[s.length()][s.length()];
//初始化每个元素子串为true
for (int i = 0; i < s.length(); i++) {
isPalindrome[i][i] = true;
}
//判断长度为2的子串
for (int i = 0; i < s.length() - 1; i++) {
isPalindrome[i][i+1] = s.charAt(i) == s.charAt(i+1);
if(isPalindrome[i][i+1]){
longest = 2;
longestPalindrome = s.substring(i,i+2);
}
}
//判断剩余子串
for (int i = s.length() - 3; i >= 0; --i) {
for (int j = i + 2; j < s.length(); ++j) {
isPalindrome[i][j] = isPalindrome[i+1][j-1]
&& s.charAt(i) == s.charAt(j);
if(isPalindrome[i][j] && j-i+1 > longest){
longest = j-i+1;
longestPalindrome = s.substring(i, j+1);
}
}
}
return longestPalindrome;
}
}