题目
最美字符串
题目描述
字符串有很多格式,我们定义最美字符串为:能够对称的
比如“CBABC“, 因为字符串本身和它的反转字符串是相同的,也即字符串以中间字符为轴心呈对称展开。
现在需要您帮忙提供方案能够将任一字符串经过最少切割次数使得每个子字串都是最美字符串。
输入说明
程序从当前路径下的data.txt文件中读取测试数据。
文件内容只有一行待测字符串(只包含数字和字母,且区分大小写, 字符串长度不超过1KB)。
输出说明
向标准输出打印最少切割次数
示例1
输入:
ACDCDCDAD
最少需要切2次, 使得每个子串都变成最美字符串, 比如: A|CDCDC|DAD, 所以输出2
输出:
2
示例2
输入:
ABA
不需要切割, 字符串本身就是对称的, 所以输出0。
输出:
0
思路
- 这是一道动态规划问题,别用贪心~比如abacccaha,用贪心每次去分割最大的回文字符串显然是错误的。
- 我们设置 dp[j] 表示从0到 j 的最小的分割数,每次递增 j ,用 i 遍历 j 到 0,寻找是否有回文字符串 S i j S_{ij} Sij ,如果有,其可能的最小值为 dp[i-1] + 1。我们遍历所有可能,每次与 dp[j] 比较,则 dp[j] = min{dp[j], dp[i-1]+1},当然需要判断一下边界 i=0 的情况。
- 中间我们用二维数组 p[i][j] 记录 i 到 j 的子串是否为回文字符串。
- 时间复杂度O(n^2)
代码
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
using namespace std;
int getMin(string s) {
int len = s.length();
int *dp = new int[len];
vector<vector<int>> p(len, vector<int>(len));
for (int j = 0; j<len; j++) {
dp[j] = 999;
for (int i = j; i >= 0; i--) {
if (s[i] == s[j] && (j - i<2 || p[i + 1][j - 1])) {
p[i][j] = true;
int t = (i == 0) ? 0 : (dp[i - 1] + 1);
if (t < dp[j]) dp[j] = t;
}
}
}
return dp[len - 1];
}
int main() {
string s;
fstream file("data.txt");
file >> s;
cout << getMin(s) << endl;
system("pause");
return 0;
}