题目难度:二星
考察点:动态规划

方法1:暴力、二进制枚举

1. 分析

我们分析一下题意,对于每个输入的字符串s,它的子串(包括不连续的子串)有2^n个,其中n为字符串s的长度,那么我们就可以枚举这2^n个子串,判断当前枚举到子串是不是回文字符串,如果是回文字符串的话,就记录答案,并取得长度最大值进行比较,否则继续枚举。

算法实现:
(1). 写一个判断字符串是不是回文字符串的函数;
(2). 对这2^n个子串进行枚举;
(3). 如果当前枚举的字符串是回文字符串的话,就更新长度最大值ans;
(4). 输出答案ans。

2. 复杂度分析:

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

3. 代码:

#include <bits/stdc++.h>
using namespace std;
bool check(string s) {
 int len = s.size();
 for(int i=0; i<len/2; i++)  if(s[i] != s[len-1-i]) return false;
 return true;
}
int main() {
 string s; cin>>s;
 int len = s.size(), ans = 0;
 int all = (1<<len);
 for(int i=0; i<all; i++) {
     string tmp = "";
     for(int j=0; j<len; j++) if(i&(1<<j)) tmp += s[j];
     if(check(tmp)) ans = max(ans, int(tmp.size()));
 }
 cout<<ans<<endl;
 return 0;
}

方法2:动态规划

1. 分析

由于上述算法的时间复杂度实在是太高了,所以我们要进行优化,采用动态规划的算法,我们令dp[i][j]表示区间[i,j]中包含的非连续最长回文字符串长度,那么就有如下两种情况:
(1). 如果第i个字符和第j个字符相等的话,那么dp[i][j] = dp[i+1][j-1] + 2;
(2). 如果第i个字符和第j个字符不相等的话,那么dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
所以动态转移方程已经在上述情况纵列出来了。

算法实现:
(1). 输入字符串s,并取得s的长度len
(2). 进行动态规划,即两层循环,外层循环枚举j(0<=j<len)表示以j字符串结尾,内层循环枚举i(j>i>=0)表示以i字符串开始,初始化一下dp[j][j],因为只有一个字符,显然dp[j][j]=1,然后在内层循环中判断s[j]和s[i]是否相等,根据是否相等进行上述方法中的动态转移。
(3). 输出答案dp[0][len-1]。

2. 复杂度分析:

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


3. 代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e3+5;
int dp[MAXN][MAXN];
int main() {
 string s; cin>>s;
 int len = s.size();
 for(int j=0; j<len; 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][len-1]<<endl;
 return 0;
}