「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。
问题一:一个字符串至少需要添加多少个字符能整体变成回文串
问题二:返回问题一的其中一种添加结果
一、分析
范围尝试模型,原始字符串是固定长度,所以从L...R这段要变成回文串,至少需要添加字符
f(str,L,R):str[L...R]上,添加的字符数
L<=R 有效,L> R 无效
对角线都是0,一个字符(L == R),L = R - 1时,两个字符(倒数第二条斜线),需要添加1个,比如ab,不等添加一个(aba 或 bab),aa相等不需要添加。
dp[i][j]
dp[i][j-1],最后j,在开始位置添加一个,dp[i][j-1] + 1dp[i+1][j],开始i,在结尾位置添加一个,dp[i+1][j] + 1dp[i+1][j-1],i == j
a b c b b
0 1 2 3 4
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 1 | 2 |
| 1 | × | 0 | 1 | 0 | 1 |
| 2 | × | × | 0 | 1 | 1 |
| 3 | × | × | × | 0 | 0 |
| 4 | × | × | × | × | 0 |
L≠R时,当前格子 = min(左边格子,下边的格子) + 1
L=R时,当前格子 = 左下格子
剩下的格子,从下到上,从左到右,分三种情况依次填好
dp[0][4]位置上的值就是需要添加几个字符
二、实现
// 问题一
public static int minInsertions(String s) {
if (s == null || s.length() < 2) {
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
int[][] dp = new int[N][N];
for (int i = 0; i < N - 1; i++) {
dp[i][i + 1] = str[i] == str[i + 1] ? 0 : 1;
}
for (int i = N - 3; i >= 0; i--) {
for (int j = i + 2; j < N; j++) {
// 可能性一、可能性二决策
dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1;
if (str[i] == str[j]) { // 可能性三
dp[i][j] = Math.min(dp[i][j], dp[i + 1][j - 1]);
}
}
}
return dp[0][N - 1];
}
// 问题二,返回其中一种结果
public static String minInsertionsOneWay(String s) {
if (s == null || s.length() < 2) {
return s;
}
char[] str = s.toCharArray();
int N = str.length;
int[][] dp = new int[N][N];
for (int i = 0; i < N - 1; i++) {
dp[i][i + 1] = str[i] == str[i + 1] ? 0 : 1;
}
for (int i = N - 3; i >= 0; i--) {
for (int j = i + 2; j < N; j++) {
dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1;
if (str[i] == str[j]) {
dp[i][j] = Math.min(dp[i][j], dp[i + 1][j - 1]);
}
}
}
int L = 0;
int R = N - 1;
char[] ans = new char[N + dp[L][R]];
int ansl = 0;
int ansr = ans.length - 1;
while (L < R) {
if (dp[L][R - 1] == dp[L][R] - 1) {
ans[ansl++] = str[R];
ans[ansr--] = str[R--];
} else if (dp[L + 1][R] == dp[L][R] - 1) {
ans[ansl++] = str[L];
ans[ansr--] = str[L++];
} else {
ans[ansl++] = str[L++];
ans[ansr--] = str[R--];
}
}
if (L == R) {
ans[ansl] = str[L];
}
return String.valueOf(ans);
}
三、总结
动态规划、范围尝试模型