最大回文子串是被研究得比较多的一个经典问题。最近月神想到了一个变种,对于一个字符串,如果不要求子串连续,那么一个字符串的最大回文子串的最大长度是多少呢。
数据范围:字符串长度满足 ,字符串中仅包含 0~9 和大小写字母
每个测试用例输入一行字符串(由数字0-9,字母a-z、A-Z构成),字条串长度大于0且不大于1000.
输出该字符串的最长回文子串的长度。(不要求输出最长回文串,并且子串不要求连续)
adbca
3
因为在本题中,不要求回文子串连续,故最长回文子串为aba(或ada、aca)
因为不要求子串连续,所以字符串abc的子串有a、b、c、ab、ac、bc、abc7个
#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; }
#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))
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; }
s = input() n = len(s) dp = [[0 for i in range(n)] for j in range(n)] for i in range(n-1, -1, -1): dp[i][i] = 1 for j in range(i+1, n): if s[i]==s[j]: dp[i][j] = 2+dp[i+1][j-1] else: dp[i][j] = max(dp[i+1][j], dp[i][j-1]) print(dp[0][-1])