hihocoder 1323 回文字符串 区间dp OR 记忆化搜索

题目链接


描述


给定一个字符串 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;
}


你可能感兴趣的:(dp,区间dp)