7.以最小插入次数构造回文串
以最小插入次数构造回文串(难度:中等)
比如说如一个s="abcea",算法返回2,插入两个字符="abecba"或者"aebcbea"。如果输入"aba" 那么返回0,本身就是回文串,无需操作
思路分析
回文串一般都是字符串从中间向两端扩散,构造回文串也是类似的
dp数组定义
上节课说了:字符串问题一般都是 二维dp
dp[i][j]:对于字符串s[i...j]最少进行dp[i][j]次操作才能成文 回文串
i----j ,定义的是区间
base case
如果想求是整个s的最小插入次数,根据这个定义,也就是求dp[0][n-1]的大小
同时 base case 也很容易想到,i==j时候,dp[i][j]==0,因为i==j时,s[i...j]就是一个字符,本身就是回文串,所以不需要进行任何操作
状态转移方程
状态转移方程就是 从小规模的问题 推出 最终大问题的解的答案,从 base case 推导
如果想要计算 dp[i][j]的值,而且假设已经计算出来了子问题dp[i+1][j-1](i,j因为是区间,在逐渐缩小)的值,能不能想办法推出 dp[i][j]?
一种情况 S[i]==s[j]
if(s[i]==s[j]){
//因为已经是回文串了,缩小成子问题
//i++ ;j--
dp[i][j]=dp[i+1][j-1];
}
另一种情况 S[i]!=s[j]
if(s[i]!=s[j]){
dp[i][j]=dp[i+1][j-1]+2;
}
正常序列 x b a a b y
发现 x != y
将y插入到x的右边,x插入到y的右边,(实际没修改,只是记录步数)
两步操作,所以+2,然后继续处理 子问题 i+1和j-1的区间[i+1,j-1]
不对,比如下面的两种情况
只需要插入一个就可以变成回文串
步骤1、做选择,先将s[i..j-1]或者s[i+1..j-1]变成回文串。
怎么做选择呢?
谁变成回文串,插入的字符少,就选择谁.
比如上面图2,s[i+1..j]变成回文串的代价小,因为本身就是回文串,根本不需要插入;同理,对于图3,将s[i..j-1]变成回文串的代码小
然而,如果s[i+1..j]和s[i..j-1]都不是回文串,都至少需要插入一个字符才能变成回文串,所以选择那个都一样
怎么知道s[i+1..j]和s[i..j-1]哪个代价小呢?
回头看看dp数组的定义,不就是变成回文串的 代价吗????所以 取min 就好啦
步骤2、根据步骤一的选择,将s[i..j]变成回文串
如果在步骤1 中 选择把s[i+1..j]变成回文串,那么在s[i+1..j]右侧插入一个 s[i]一定可以将 s[i..j]变成回文的;
同理;选择把s[i..j-1]变成回文串,那么在s[i..j-1]左侧插入一个 s[j]一定可以将 s[i..j]变成回文的,
一种情况 S[i]!=s[j]
if(s[i]!=s[j]){
//步骤一选择代价小的
//步骤二必然要进行一次插入
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
}
综合起来状态转移方程
if(s[i]==s[j]){
//因为已经是回文串了,缩小成子问题 //i++ ;j--
dp[i][j]=dp[i+1][j-1];
}
if(s[i]!=s[j]){
//步骤一选择代价小的
//步骤二必然要进行一次插入
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
}
完整代码如下
//从下至上
//从左至右
略
自己写