在学校做OJ的时候偶然遇到一到求回文子串的题。研究发现,分为连续和不连续回文子串两种求解情况。
推荐参考这篇(算法:最长回文长度_hairuiJY的博客-CSDN博客_最长回文串长度)和
(最长回文子序列问题(两种方法) | 春水煎茶 - 王超的个人博客);
这里的连续意思是,子串的取法,必须是相邻的字符组成。
不连续这和下面这道题一样,从原字符串中抽取一部分字符组成新的子串。或者如题所述相当于删去一部分字符,剩下的为子串。
首先求解连续的回文子串
代码如下
#include<bits/stdc++.h>
using namespace std;
#define MAX 100
int dp[MAX][MAX];
int main() {
string s;
while (cin >> s)
{
int len = s.length();
// dp 数组初始化
for (int i = 0; i < len; i++)
{
dp[i][i] = 1;
if (s[i] == s[i + 1]) {
dp[i][i + 1] = 1;
}
}
//从下向上找 奇数的回文数组 一定从 a 这种单字母首尾累加相同字母组成。
// 偶数回文数组 一定从 aa 这种双字母开始
// 一开始初始化 找到了母串的子串。
int MinLen = MAX;
for (int i = len - 1 - 2; i >= 0; i--)
{
//从倒数第三行开始搜索
for (int j = i + 2; j < len; j++) {
//朝左上↖搜索
if (s[i] == s[j] && dp[i + 1][j - 1] == 1) {
dp[i][j] = 1;
if (MinLen > j - i + 1)
{
MinLen = j - i + 1;
}
}
}
}
if (MinLen == MAX)
{
cout << 1 << endl;
}
else {
cout << MinLen << endl;
}
}
return 0;
}
不连续的情况,就和这个题的解法一并给出吧。
题目
[2017 NUIST 程序设计竞赛]
D. 最长回文长度
题目描述
回文字符串就是从前往后读和从后往前读都一样的字符串。比如 abcba, aaa, QoQ。现在给你一个字符串,你可以从中删去一些字符,在不改变原来字符相对顺序的情况下,得到一个最长的回文字符串。
比如 abxdba, 删去字符 x 后,可以得到 abdba,是一个回文字符串。你的任务就是求出给定的字符串删去若干字符后可以得到的最长的回文字符串的长度。字符串长度不超过 1000,字符范围为 ‘a’ 到 ‘z’。
输入描述
有多组输入。
每组一行字符串。
输出
每组测试数据输出一个整数,表示可以得到的最长的回文字符串的长度。
样例输入
aabbaabb
computer
abzla
samhita
样例输出
6
1
3
3
代码如下
#include<bits/stdc++.h>
using namespace std;
#define MAX 100
int dp[MAX][MAX];
int main() {
//相当于求解 一个字符串和他的逆序字符串的 最长公共子序列
string s;
while (cin >> s)
{
int len = s.length();
//需要初始化
// 下标为0 代表考虑零个字符。不然的话 dp[i-1][j-1]超出数组了。
for (int i = 0; i <= len; i++)
{
dp[i][0] = 0;
dp[0][i] = 0;
}
//注意这里的下标关系 字符串时从零比较,但是dp[1]代表的是 考虑加入s[0]处的字符的情况。
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len; j++)
{
if (s[i] == s[len - 1 - j]) {
dp[i + 1][j + 1] = dp[i][j] + 1;
}
else {
//得到小一级规模中 最长的情况
dp[i + 1][j + 1] = dp[i + 1][j] > dp[i][j + 1] ? dp[i + 1][j] : dp[i][j + 1];
}
}
}
for (int i = 0; i <= len + 1; i++)
{
for (int j = 0; j <= len + 1; j++) {
cout << dp[i][j] << " ";
}
cout << endl;
}
cout << dp[len][len] << endl;
}
return 0;
}
运行结果如下图