题目链接
给定一个字符串 S ,最少需要几次增删改操作可以把 S 变成一个回文字符串?
一次操作可以在任意位置插入一个字符,或者删除任意一个字符,或者把任意一个字符修改成任意其他字符。
思路:
考虑记忆化搜索,dp[l][r] 表示从l开始到r结束的字符串构成回文串需要的最少操作次数,
1. 那么如果s[l] == s[r] dp[l][r] = dp[l+1][r-1];
2.如果s[l] != s[r] 那么我们就要考虑添加一个字符,删除一个字符,修改一个字符.考虑到删除和添加字符其实是等价
的,这里只说添加.
dp[l][r] = min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1])+1
dp[l+1][r] 表示在s[r]后面添加一个字符匹配s[l]
dp[l][r-1] 表示在s[1]前面添加一个字符匹配s[r]
dp[l+1][r-1] 表示把s[l] s[r]其中的任何一个修改为另一个.
#include
using namespace std;
const int N = 105;
int dp[N][N];
char s[N];
int min(int a, int b, int c)
{
return min(min(a, b), c);
}
int dfs(int l,int r)
{
if(l >= r) return dp[l][r] = 0;
if(dp[l][r] != -1) return dp[l][r];
if(s[l] == s[r])
return dp[l][r] = dfs(l+1,r-1);
return dp[l][r] = 1+min(dfs(l+1,r),dfs(l,r-1),dfs(l+1,r-1));
}
int main()
{
scanf("%s",s+1);
int len = strlen(s+1);
memset(dp, -1, sizeof(dp));
dfs(1,len);
cout<
当然也可以区间dp,其实和上面的本质是一样的,都是先让最小的(每两个字符)组成回文,再往大了去扩展.
#include
#include
#include
using namespace std;
char s[105];
int dp[105][105];
int min(int a,int b,int c)
{
return min(a,min(b,c));
}
int main()
{
scanf("%s", s);
int n = strlen(s);
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])
dp[i][j] = dp[i+1][j-1];
else
dp[i][j] = min(dp[i+1][j],dp[i][j-1],dp[i+1][j-1]) + 1;
}
}
printf("%d\n", dp[0][n-1]);
return 0;
}