问题描述
小明正在玩一个字符串拼图游戏,他有一个由小写字母组成的字符串。游戏规则允许他进行两种操作:1) 删除任意一个字符;2) 在任意位置插入任意一个小写字母。每次操作都会消耗1点能量。现在小明想要通过最少的操作次数,使得字符串变成回文串(即正着读和反着读都一样)。请你帮助小明计算最少需要多少次操作。
要求:
- 设计一个动态规划算法来计算最小操作次数。
- 算法的时间复杂度应为 O(n²),其中 n 是字符串的长度。
提示 :
最小操作次数等于字符串长度减去该字符串的最长回文子序列的长度。
例如,字符串 "abcda" 的最长回文子序列是 "aca" 或 "ada",长度为3,所以最小操作次数为 5 - 3 = 2。
你可以使用动态规划来求解最长回文子序列的长度。
测试样例
样例1:
输入:
s = "abcda"输出:2解释:最长回文子序列为 "aca" 或 "ada",长度为3,所以最小操作次数为 5 - 3 = 2。一种可行方案是删除 'b' 和 'c'(2次操作)得到 "ada"。
样例2:
输入:
s = "abcd"输出:3解释:最长回文子序列为 "a"(或 "b"、"c"、"d"),长度为1,所以最小操作次数为 4 - 1 = 3。一种可行方案是删除 'b'、'c'、'd'(3次操作)得到 "a"。
样例3:
输入:
s = "a"输出:0解释:单个字符已经是回文串,最长回文子序列长度为1,所以最小操作次数为 1 - 1 = 0。
约束条件
- 1 ≤ s.length ≤ 1000
- 字符串 s 仅包含小写英文字母
- 保证输入字符串非空
程序代码
#include <stdio.h>
#include <string.h>
int minOperations(char* s) {
int n = strlen(s);
if (n <= 1) return 0;
// dpij 表示 si..j 的最长回文子序列长度
int dp10001000;
// 初始化:单个字符长度为1
for (int i = 0; i < n; i++) {
dpii = 1;
}
// 按长度递增计算
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
if (si == sj) {
if (len == 2) {
dpij = 2;
} else {
dpij = dpi+1j-1 + 2;
}
} else {
dpij = (dpi+1j > dpij-1) ? dpi+1j : dpij-1;
}
}
}
int lps = dp0n-1;
return n - lps;
}
int main() {
printf("%d\n", minOperations("abcda")); // 2
printf("%d\n", minOperations("abcd")); // 3
printf("%d\n", minOperations("a")); // 0
return 0;
}
cpp
#include <stdio.h>
#include <string.h>
int minOperations(char* s) {
int n = strlen(s);
if (n <= 1) return 0;
// dp[i][j] 表示 s[i..j] 的最长回文子序列长度
int dp[1000][1000];
// 初始化:单个字符长度为1
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
// 按长度递增计算
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
if (s[i] == s[j]) {
if (len == 2) {
dp[i][j] = 2;
} else {
dp[i][j] = dp[i+1][j-1] + 2;
}
} else {
dp[i][j] = (dp[i+1][j] > dp[i][j-1]) ? dp[i+1][j] : dp[i][j-1];
}
}
}
int lps = dp[0][n-1];
return n - lps;
}
int main() {
printf("%d\n", minOperations("abcda")); // 2
printf("%d\n", minOperations("abcd")); // 3
printf("%d\n", minOperations("a")); // 0
return 0;
}
运行结果
