最大回文子串是被研究得比较多的一个经典问题。最近月神想到了一个变种,对于一个字符串,如果不要求子串连续,那么一个字符串的最大回文子串的最大长度是多少呢。
数据范围:字符串长度满足
,字符串中仅包含 0~9 和大小写字母
每个测试用例输入一行字符串(由数字0-9,字母a-z、A-Z构成),字条串长度大于0且不大于1000.
输出该字符串的最长回文子串的长度。(不要求输出最长回文串,并且子串不要求连续)
adbca
3
因为在本题中,不要求回文子串连续,故最长回文子串为aba(或ada、aca)
因为不要求子串连续,所以字符串abc的子串有a、b、c、ab、ac、bc、abc7个
#include <iostream>
#include<bits/stdc++.h>
#define N 1000
using namespace std;
//可以使用递归的算法,但是这种算法对于字符串较大的情况会通不过,但是思路很简单
int dp(char*s,int start, int end){
if (start>end){
return 0;
}else if(start==end){
return 1;
}else{
if(s[start]==s[end]){
return dp(s,start+1,end-1)+2;
}else{
return max(dp(s,start+1,end),dp(s,start,end-1));
}
}
}
//使用动态规划的思想,d[i][j]表示字符串s中位置j到位置i(i>j)的字符串中的最长的回文长度,
//当s[i]==s[j]时,d[i][j]的最长回文字符串长度为其子串d[i-1][j+1]的长度+2
//当s[i]!=s[j]时,d[i][j]的最长回文字符串长度为d[i-1][j]和d[i][j+1]两个中的最大值
//最后,d[n-1][0]就是最后的长度
int dp(char*s){
int n =strlen(s);
int i,j;
int d[N][N];
for(i=0;i<n;i++){
d[i][i]=1;
for(j=i-1;j>-1;j--){
if(s[i]==s[j]){
d[i][j]=d[i-1][j+1]+2;
}else{
d[i][j]=max(d[i-1][j],d[i][j+1]);
}
}
}
return d[n-1][0];
}
int main(){
char s[1000];
cin>>s;
cout<<dp(s)<<endl;
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 求最长回文子串的问题,一般有两种方法
* 1.动态规划
* 2.中心扩散方法
*/
public class Solution8_回文字符串 {
/**
* 最长文回串 ,非连续
* 动态规划
*/
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String s = bf.readLine();
int n = s.length();
int[][] dp = new int[n][n];//dp[l][r]表示l-r中的最长回文串
for (int r = 0; r < n; r++) {
dp[r][r] = 1;
for (int l = r - 1; l >= 0; l--) {
if (s.charAt(l) == s.charAt(r)) {
dp[l][r] = dp[l + 1][r - 1] + 2;
}else {
dp[l][r] = Math.max(dp[l+1][r],dp[l][r-1]);
}
}
}
System.out.println(dp[0][n-1]);
}
} 既然刷到了这个题,还是要介绍一下最原始的求解最长回文子串的问题,即回文串是连续的。
public class Solution144_5_最长回文子串 {
/**
* 方法一:中心扩展法
* 1.既然是回文字符串,本来联想的是滑动窗口,双指针这样的思想,发现并不行,
* 无法确保 abcd...这一段是否是回文串,万一继续后面是dcba,总不能全部遍历吧,那就等于暴力算法了。
* 2.回文串的特点,由中间到两边扩散,aba 或者 bb这样的形式,这就需要我们分情况讨论了。
* for循环以每一个点为中心,求最长文回串,从中心点向两边延伸,记录最长子串
*/
private int start, maxLen;
public String longestPalindrome(String s) {
if (s == null || s.length() < 2) return s;
for (int i = 0; i < s.length(); i++) {
//考虑两种情况1:aba 和 2: bb
findMaxPalindrome(s, i, i);
findMaxPalindrome(s, i, i + 1);
}
return s.substring(start, start + maxLen);
}
private void findMaxPalindrome(String s, int i, int j) {
while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) {
i--;//向左延伸
j++;//向右延伸
}
//记录每个起始点扩展的回文串的最大长度
if (maxLen < j - i - 1) {
start = i + 1;
maxLen = j - i - 1;
}
}
/**
* 方法二:动态规划
* 求解 "最优子结构" 问题,可以考虑用 "动态规划"
*/
public String longestPalindrome1(String s) {
int n = s.length();
if (n < 2) return s;
int maxLen = 1;
String res = s.substring(0, 1);
boolean[][] dp = new boolean[n][n];
//左边界一定小于右边界,因此从右边界开始
for (int r = 1; r < n; r++) { //表示右边界
for (int l = 0; l < r; l++) { //表示左边界
// 区间应该慢慢放大
// 状态转移方程:如果头尾字符相等并且中间也是回文
// 在头尾字符相等的前提下,如果收缩以后不构成区间(最多只有 1 个元素),直接返回 True 即可
// 否则要继续看收缩以后的区间的回文性
if (s.charAt(l) == s.charAt(r) && ((r - l) <= 2 || dp[l + 1][r - 1])) {
dp[l][r] = true;
if (r - l + 1 > maxLen) {
maxLen = r - l + 1;
res = s.substring(l, r + 1);
}
}
}
}
return res;
}
}
/*
考虑动态规划
dp[i][j]表示第i个字符到第j个字符中包含的最大回文子串的最大长度
i:j->0若a[i]与a[j]有相同的字符,则最大长度为dp[i+1][j-1]+2;
否则为以下最大值 max(dp[i+1][j],dp[i][j-1])
*/
#include<bits/stdc++.h>
using namespace std;
#define N 1000
char a[N];
int dp[N][N];
int main()
{
// freopen("input.txt", "r", stdin);
int n, i, j, k;
cin >> a;
n = strlen(a);
for(j = 0; j < n; j++) {
dp[j][j] = 1;
for(i = j - 1; i >= 0; i--) {
if(a[i] == a[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
cout << dp[0][n - 1] << endl;
return 0;
}
def dpMaxLenHuiwen(s): if not s: return n = len(s) if n == 1: return 1 # d[i][j]为从j到i的字符串中包含的最大回文子串的长度 d = [[0 for i in range(n)] for j in range(n)] for i in range(n): d[i][i] = 1 # 初始化s[i]=1,因为一个字符串的回文就是1 for j in range(i-1,-1,-1): # if(s[i] == s[j]): d[i][j] = d[i-1][j+1] + 2 # 两边相等,最大回文长度+2 else: d[i][j] = max(d[i-1][j], d[i][j+1]) #两边不相等,最大回文长度为两个最大子串中的最大回文长度 return d[n-1][0] s = input() print(dpMaxLenHuiwen(s))
/*
动态规划。
dp[i][j]表示第i个字符到第j个字符中包含的非连续回文长度。
转移方程:
在一个串后面加一个字符x,如果前面没有和x一样的,那包含的非连续回文长度和没加x之前一样。
如果有和x一样的,那需要比较没加x之前的长度 和 两个x之间的回文长度+2 两者的大小。
即dp[beg][end] = Math.max(dp[beg][end - 1], dp[loc + 1][end - 1] + 2)
*/
import java.util.*;
public class Main {
static String s;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
s = in.next();
int len = s.length();
int[][] dp = new int[len + 1][len + 1];
dp[0][0] = 0;
for (int i = 1; i <= len; i++) {
dp[i][i] = 1;
}
for (int end = 1; end <= len; end++)
for (int beg = 1; beg < end; beg++) {
dp[beg][end] = dp[beg][end - 1];
for (int loc = beg; loc < end; loc++) {
if (s.charAt(loc - 1) == s.charAt(end - 1)) {
dp[beg][end] = Math.max(dp[beg][end - 1], dp[loc + 1][end - 1] + 2);
break;
}
}
}
System.out.println(dp[1][len]);
}
}
def max_sub_str_length(raw_str):
dp = {}
def length_from_l_to_r(l, r):
if l == r:
return 1
if l > r:
return 0
if (l, r) in dp:
return dp[(l, r)]
sub_str_length = 0
if raw_str[l] == raw_str[r]:
sub_str_length = 2 + length_from_l_to_r(l+1, r-1)
else:
sub_str_length = max(length_from_l_to_r(l+1, r), length_from_l_to_r(l, r-1))
dp[(l, r)] = sub_str_length
return sub_str_length
return length_from_l_to_r(0, len(raw_str)-1)
raw_str = input()
print(max_sub_str_length(raw_str)) #include <bits/stdc++.h>
using namespace std;
int main(){
string s;
cin>>s;
int n = s.length();
int dp[n][n];
memset(dp,0,sizeof(dp));
for(int j=0;j<n;j++){
dp[j][j] = 1;
for(int i=j-1;i>=0;i--){
if(s[i] == s[j])
dp[i][j] = dp[i+1][j-1] + 2;
else
dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
}
}
cout<<dp[0][n-1]<<endl;
return 0;
}
s = input() dp = [[0 for _ in range(len(s))] for _ in range(len(s))] for i in range(len(s) - 1, -1, -1): dp[i][i] = 1 for j in range(i + 1, len(s)): if s[i] == s[j]: dp[i][j] = dp[i + 1][j - 1] + 2 else: dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]) print(dp[0][-1])
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
int longestPalindromeSubseq(string& s)
{
vector<vector<int>> dp(s.size() + 1, vector<int>(s.size() + 1));
for(int i = 1; i <= s.size(); i++)
{
for(int j = 1; j <= s.size(); j++)
{
if(s[i - 1] == s[s.size() - j])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[s.size()][s.size()];
}
int main()
{
string s;
cin >> s;
cout << longestPalindromeSubseq(s) << endl;
return 0;
} #include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
string str;
cin >> str;
int n = str.length();
int dp[n][n] = {0}; //dp[l][r]表示l-r中的最长回文串
for(int r = 0; r < n; r++)
{
dp[r][r] = 1;
for(int l = r-1; l >= 0; l--)
{
if(str[l] == str[r])
{
dp[l][r] = dp[l+1][r-1]+2;
}
else
{
dp[l][r] = max(dp[l+1][r],dp[l][r-1]);
}
}
}
cout << dp[0][n-1] << endl;
return 0;
} import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while (input.hasNext()) {
String s = input.next();
char[] c = s.toCharArray();
int len = c.length;
int[][] dp = new int[len][len];
for (int r = 0; r < len; r++) {
dp[r][r] = 1;
for (int l = r - 1; l >= 0; l--) {
if (c[r] == c[l]) {
dp[l][r] = dp[l + 1][r - 1] + 2;
} else {
dp[l][r] = Math.max(dp[l + 1][r], dp[l][r - 1]);
}
}
}
System.out.println(dp[0][len - 1]);
}
}
} #include <iostream>
#include <istream>
#include <string>
#include <vector>
using namespace std;
int main(){
string s;
cin >> s;
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
for(int i = 0; i<n; i++){
dp[i][i] = 1;
}
for(int i = 1; i<n; i++){
for(int j = i-1; j>=0; j--){//注意一定要从近到远
if(s[i] == s[j]){
if(j+2 <= i) dp[j][i] = max(dp[j][i], dp[j+1][i-1]+2);//j,i不挨着
else dp[j][i] = 2; //j,i挨着
} else{
//j,i之间的最长回文要么是j+1~i,要么是j~i-1
dp[j][i] = max(dp[j][i], dp[j+1][i]);
dp[j][i] = max(dp[j][i], dp[j][i-1]);
}
}
}
cout << dp[0][n-1] << endl;
return 0;
} #include <bits/stdc++.h>
using namespace std;
int f[1002][1002];
int main(){
string s;
cin >> s;
for (int i = s.length() - 1; i >= 0; i--){
for (int j = 0; j < s.length(); j++){
if(i > j){
f[i][j] = 0;
} else if(i == j){
f[i][j] = 1;
} else{
if(s[i] == s[j]){
f[i][j] = f[i+1][j-1] + 2;
} else {
f[i][j] = max(f[i+1][j], f[i][j-1]);
}
}
}
}
cout << f[0][s.length() - 1] << "\n";
return 0;
} #include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string str;
cin >> str;
int size = str.size();
int dp[size][size];
for(int i = size-1; i >= 0; i--)
{
for(int j = i; j < size; j++)
{
if(i==j)
dp[i][j] = 1;
else if(str[i] == str[j])
{
if(i+1 <= j-1)
dp[i][j] = dp[i+1][j-1] + 2;
else
dp[i][j] = 2;
}
else
{
dp[i][j] = max(dp[i][j-1], dp[i+1][j]);
}
}
}
cout << dp[0][size-1] << endl;
return 0;
} import sys def fun(string): length = len(string) if length==0 or length==1: return length else: dp = [] for i in range(length): dp.append([0]*length) for i in range(length): dp[i][i] = 1 for k in range(1,length): for j in range(length): if j + k >=length: continue else: if string[j] == string[j+k]: dp[j][j+k] = dp[j+1][j+k-1] + 2 else: dp[j][j+k] = max(dp[j+1][j+k],dp[j][j+k-1]) return dp[0][length-1] if __name__ == "__main__": string = sys.stdin.readline().strip() print(fun(string))
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
char[] chars=sc.nextLine().toCharArray();
int len=chars.length;
int[][] dp=new int[len][len];
for(int i=0;i<len;i++){
dp[i][i]=1;
}
/*
注意这里的循环,外层循环是end,内层循环是beg,顺序不能反
因为求dp[beg][end]的过程依赖于beg之后的值
*/
for(int end=1;end<len;end++){
for(int beg=0;beg<end;beg++){
dp[beg][end]=dp[beg][end-1];
for(int t=beg;t<end;t++){
if(chars[t]==chars[end]){
dp[beg][end]=Math.max(dp[beg][end-1],dp[t+1][end-1]+2);
break;
}
}
}
}
System.out.println(dp[0][len-1]);
}
}
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string s;
cin >> s;
int N = s.size();
vector<vector<int>> dp(N, vector<int>(N, 0));
/* 注释中的部分无法通过全部样例, 有人能帮忙看下吗
for(int i = 0; i < N; i++) {
dp[i][i] = 1;
if(i+1 < N && s[i] == s[i+1])
dp[i][i+1] = 2;
}
for(int len = 3; len <= N; len++) {
for(int left = 0; left + len - 1 < N; left++) {
int right = left + len - 1;
dp[left][right] = max(dp[left+1][right], dp[left][right-1]);
if(s[left] == s[right])
dp[left][right] = max(dp[left][right], dp[left+1][right-1] + 2);
}
}
*/
for(int i = 0; i < N; i++) {
dp[i][i] = 1;
for(int j = i - 1; j >= 0; j--) {
dp[j][i] = max(dp[j+1][i], dp[j][i-1]);
if(s[j] == s[i])
dp[j][i] = max(dp[j][i], 2 + dp[j+1][i-1]);
}
}
cout << dp[0][N-1] << endl;
return 0;
}
转化为求解s和s_reverse的最大公共子串
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main(){
string s;
cin >> s;
string t(s);
reverse(t.begin(), t.end());
int n = s.size();
vector<vector<int>> dp(n+1, vector<int>(n+1));
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
if(s[i] == t[j])
dp[i+1][j+1] = dp[i][j] + 1;
else
dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1]);
}
}
cout << dp[n][n] << endl;
}