【力扣】1312. 让字符串成为回文串的最少插入次数
【力扣】1312. 让字符串成为回文串的最少插入次数
给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。
请你返回让 s 成为回文串的 最少操作次数 。
「回文串」是正读和反读都相同的字符串。
示例 1:
输入:s = “zzazz”
输出:0
解释:字符串 “zzazz” 已经是回文串了,所以不需要做任何插入操作。
示例 2:
输入:s = “mbadm”
输出:2
解释:字符串可变为 “mbdadbm” 或者 “mdbabdm” 。
示例 3:
输入:s = “leetcode”
输出:5
解释:插入 5 个字符后字符串变为 “leetcodocteel” 。
提示:
1 <= s.length <= 500
s 中所有字符都是小写字母。
class Solution
{
public:
int minInsertions(string s)
{
int len = s.length();
string s2 = s;
std::reverse(s2.begin(), s2.end());
int dp[501][501];
memset(dp, 0, sizeof(dp));
for(int i=1; i<=len; i++){
for(int j=1; j<=len; j++){
dp[i][j] = s.at(i-1) == s2.at(j-1) ? dp[i-1][j-1] + 1 : std::max(dp[i-1][j],dp[i][j-1]);
}
}
return len - dp[len][len];
}
};
==================
找到越多的已经有配对的数字,那没有配对的数字就越少,需要插入的次数也就越少。
所以就转化为 找最长的回文子串。
这个题主要考一个转换(把实际问题转换为模板问题)吧
最长的回文子串 转换为 最长相同子序列
将所给的字符串翻转,用翻转后的字符串与原字符串来求最长相同子序列。所得子序列的长度,就是原串已有的最长的已经可以对称的数量。
1.凭啥这样转换就是对的。
想象一下,假设这个串是水平的, 有一条竖直的轴,原串已经对称的字母是关于这条轴对称的,没有配对的字母关于这条轴的对称位置用一个空格占位。
以这个轴来进行翻转,已经配对的这些所有字母组成的子序列,和原串中这些字母中组成的子序列是相同的!
也就是 已经配对的字母 和翻转后 这些字母组成的子序列是相同的。
哈哈这个有点绕。
那这样理解,直接把已经配对的子序列s0拿出来,s0一定是对称的吧,就是说s0一定是回文串,当s0跟随原串翻转后,由于这个串是个回文串,s0翻转后还是s0。
等价转换: (get 一下,是不是这个道理)
回文子序列 <=> 所有已配对的字母拼接的子序列
回文子序列 一定是 原串和翻转后的串 的公共子序列
还需要证明 最长的 回文子序列 一定是 最长的 原串和翻转后的串 的公共子序列
或者证明
原串和翻转后的串 的公共子序列 <=> 回文子序列
解题的时候,到这里,就可以直接直接冲一波,凭直觉,两者是等价的,看看能不能过。
哇,暂时想不到,后面再补充吧,有点累了。。。
当然,s2这个变量可以不用写,把s2.at(j-1) 替换成s.at(len-j)。
像这样写是为了更容易理解。
个人认为,在ac的前提下也可以适当增加可读性嘛。可读性高,减小思维负担,出错几率就小很多。