1、什么是回文palindrome?
回文指的是正读和反读都一样的字符串,如aba,abba等
2、字符子串和字符子序列的区别
字符字串指的是字符串中连续的n个字符;如palindrome中,pa,alind,drome等都属于它的字串
而字符子序列指的是字符串中不一定连续但先后顺序一致的n个字符;如palindrome中,plind,lime属于它的子序列,而mod,rope则不是,因为它们与字符串的字符顺序不一致。
3、最长回文子序列
要求:
给定字符串,求它的最长回文子序列长度。回文子序列反转字符顺序后仍然与原序列相同。例如字符串abcdfcba中,最长回文子序列长度为7,abcdcba或abcfcba。
leetcode 5. Longest Palindromic Substring
题目描述:
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
https://blog.csdn.net/shineboyxxb/article/details/52079360
思路一、
暴力破解:(无需掌握)
暴力破解法:时间复杂度 o(n^3)
遍历字符串子串:嵌套一个循环、O(n^2);
判断是否为回文:再次嵌套一个循环、O(n^3)。
实现:
public class longestPalindromeDemo {
public String longestPalindrome(String s) {
if(s.length()<1) return "";
String ret="";
for(int i=s.length();i>=0;i--){
for(int j=0;j<i;j++){
String sub=s.substring(j, i);
if(fun(sub)){
if(sub.length()>ret.length()){
ret=sub;
}
}
}
}
return ret;
}
private boolean fun(String sub) {
if(sub.length()==1){
return true;
}
int count=0;
for(int i=0;i<sub.length()/2;i++){
if(sub.charAt(i)==sub.charAt(sub.length()-i-1)){
count++;
}
if(count==sub.length()/2){
return true;
}
}
return false;
}
思路二、DP
则dp[j][i]为true时表示索引j到索引i形成的子串为回文子串,且子串起点索引为i,长度为j+i-1。
递推公式第3项比如:abbaabccba中当i=3,j=0时,str[i]=str[j],此时j-i是否为回文串,就需要进一步判断j+1——i-1这个区间的子串是否为回文串,若是,则j——i是,否则,不是。
实现:
class Solution {
public String longestPalindrome(String s) {
int n=s.length();
boolean[][] dp=new boolean[n][n];//保存下标为i,j的串是否是回文
int len=0;//保存最长回文子串的长度
int start=0;//保存最长回文字串的下标
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
if(i-j<2){
dp[j][i]=(s.charAt(i)==s.charAt(j));//如果是一个字符,则必为回文;如果是两个字符,则这两个字符相等也必为回文
}else{
dp[j][i]=(s.charAt(i)==s.charAt(j)&&dp[j+1][i-1]);//如果[j,i]之间相差字符超过2个,则如果j和i处字符相等,且往中间夹逼一下仍是回文,则为回文
}
if(dp[j][i]&&len<i-j+1){//如当前是回文,且长度比之前记录的最大长度大,则更新最大长度
len=i-j+1;
start=j;
}
}
}
return s.substring(start,start+len);
}
}
思路三、三指针
实现:
//定位指针+双指针
public String longestPalindrome3(String s) {
if(s.length()<2) return s;
int len=s.length();
int max_left=0;//最长回文子串的起始位置
int max_len=1;//最长回文字串的长度
int left,right;//左右指针,用来寻找回文字串的边界
int start=0;//用来重起一个字串
while(start<len&&len-start>max_len/2){
left=right=start;
//回文之间存在偶数个时的处理
while(right<len-1&&s.charAt(right+1)==s.charAt(right)){
right++;
}
start=right+1;
while(right<len-1&&left>0&&s.charAt(right+1)==s.charAt(left-1)){
right++;
left--;
}
//此时[left,right]的是一个回文,判断是否为最长,若是最长,则更新
if(max_len<right-left+1){
max_len=right-left+1;
max_left=left;
}
}
return s.substring(max_left, max_left+max_len);
}
leetcode 516. Longest Palindromic Subsequence
Given a string s, find the longest palindromic subsequence's length in s. You may assume that the maximum length of s is 1000.
Example 1:
Input:
"bbbab"
Output:
4
One possible longest palindromic subsequence is "bbbb".
Example 2:
Input:
"cbbd"
Output:
2
One possible longest palindromic subsequence is "bb".
思路参考:https://www.cnblogs.com/AndyJee/p/4465696.html
https://blog.csdn.net/qq_22222499/article/details/70757904
https://www.cnblogs.com/qcblog/p/7819393.html
解题思路:
对于任意字符串,如果头尾字符相同,那么字符串的最长子序列等于去掉首尾的字符串的最长子序列加上首尾;如果首尾字符不同,则最长子序列等于去掉头的字符串的最长子序列和去掉尾的字符串的最长子序列的较大者。
因此动态规划的状态转移方程为:
设字符串为str,长度为n,DP[i][j]表示第i到第j个字符间的子序列的个数(i<=j),则:
状态初始条件:dp[i][i]=1 (i=0:n-1)
状态转移方程:dp[i][j]=dp[i+1][j-1] + 2 if(str[i]==str[j])
dp[i][j]=max(dp[i+1][j],dp[i][j-1]) if (str[i]!=str[j])
下图为i=0,j=4时的判断过程:
实现:
public int longestPalindromeSubseq(String s) {
int n=s.length();
int[][] dp=new int[n][n];
for(int j=0;j<n;j++){
dp[j][j]=1;
for(int i=j-1;i>=0;i--){
if(s.charAt(i)==s.charAt(j)){
dp[i][j]=dp[i+1][j-1]+2;
}else{
dp[i][j]=Math.max(dp[i][j-1], dp[i+1][j]);
}
}
}
return dp[0][n-1];
}