题目
-
遍历模型为:遍历每一个范围的子串[i,j]
-
如果 i = j,此时子串长度为1,不需要添加字符;
-
如果 [i,i+1] ,即子串长度为2,如果两个字符相等,不需要添加字符,如果不相等,只需要添加其中1字符就能构成回文
-
如果是其余情况,进行可能性分析,分为3种情况
- [i,j] 范围,先让 [i,j-1] 范围构成回文,然后在 i 的左边添加 j 位置的字符构成回文, dp[i][j-1]+1
- [i,j] 范围,先让 [i+1,j] 范围构成回文,然后在 j 的右边添加 i 位置的字符构成回文,dp[i+1][j]+1
- [i,j] 范围,如果 i、j 位置字符相等,那么只需 [i+1,j-1] 范围构成回文即可,dp[i+1][j-1]
-
当填完 dp 表,得到整个字符串最小需要添加的字符数量后,根据 dp 表开始回退路径,从右上角开始(右上角为最少需要添加的字符数答案)
-
如果答案来自左边,则说明是[i,j]范围内, [i,j-1] 范围构成回文,然后在 i 的左边添加 j 位置的字符构成回文这种情况,所以数组的首尾字符为 j 位置字符,然后首位指针向右左锁进
-
来到上一步确立答案来源的位置,继续判断来自可能性分析中的哪种情况,然后填写左右指针位置的字符,直到遍历到起始位置
-
如果是要求所有添加字符的情况,那在遇到比如可以选左也可以选右情况时,进行深度优先遍历走完所有情况统计答案
function process(str) {
let dp = [];
let row = str.length,
col = str.length,
res = Infinity;
// 区间长度为1时,已经是回文,不用添加
for (let i = 0; i < row; i++) {
dp[i][j] = 0;
}
// 当区间长度为2时,如果字符相等不用添加,字符不等只需要添加两个字符中其中1个就能构成回文
for (let i = 1; i < row; i++) {
if (str[i] === str[i - 1]) {
dp[i][i - 1] = 0;
} else {
dp[i][i - 1] = 1;
}
}
// 其余情况
// 从下往上,从右往左填
for (let row = str.length - 3; row >= 0; row--) {
for (let col = str.length - 1; cor >= 2; col--) {
// 只填写上半区中间对角线和中间对角线右边的对角线以上的区域
if (row + 2 > col) {
break;
}
res = Math.min(res, dp[row][col - 1] + 1);
res = Math.min(res, dp[row + 1][col] + 1);
if (str[row] === str[col]) {
res = Math.min(res, dp[row + 1][col - 1]);
}
}
}
// 返回 0 - (字符串长度-1) 的范围就是整个字符串变成回文的最少添加情况
const minNum = dp[0][row - 1];
redo(str, minNum);
// 根据 dp 表还原路径
function redo(str, minNum) {
const len = str.length + minNum;
const arr = Array(len);
let left = 0,
right = len - 1,
row = 0,
col = str.length - 1;
// 终止情况也可以是抵达对角线上时
while (left < right) {
let temp = dp[row][col];
let left = dp[row][col - 1];
let down = dp[row + 1][col];
// 说明 [i,j] 范围内,是 [i,j-1] 构成回文,在最左边添加 j 位置字符构成回文的情况
if (temp + 1 == left) {
arr[left] = str[col];
arr[right] = str[col];
row = row;
col = col - 1;
}
// 说明 [i,j] 范围内,是 [i+1,j] 构成回文,在最右边添加 i 位置字符构成回文的情况
if (temp + 1 == down) {
arr[left] = str[row];
arr[right] = str[row];
row = row + 1;
col = col;
}
// 说明是 i、j 字符相等,[i+1,j-1] 构成回文的情况
if (str[row + 1] === str[col - 1]) {
arr[left] = str[row];
arr[right] = str[row];
row = row + 1;
col = col - 1;
}
left++, right--;
}
}
}