区间DP_ HDU2476

当给的两个字符串没有一个对应位置上的字符是相等的话,那么如果从str1转换为str2,也就相当于一个空白的字符串转换为其中任意一个字符串

如果在我们枚举的区间[ 0 , i ]中第i个字符是相同的,那么实质上这里就和我们直接更新区间 [ 0 , i-1 ]是相同的,这样的话,我们也只是可以更新开头位置必须为0的相应区间    

但是当我们中间存在相应位置字符相同的话,我们可以枚举中间某个位置k,寻找最优解(最终得到的位置就是在[k+1,i]区间中,没有和str1中相同的字符),并且一定会得到最终的最优解

算法解释

首先是将一个空白字符串转换为str2:dp[i][j] :表示在区间[i , j] 中最少的转变次数,我们可以简单的想到 :dp[i][j] = dp[i][k-1] + dp[k][ j] ,但是当str2[i] == str2[k]时,这个式子还是可以优化的,所以上面这个式子就是不对的,而是:

if(str2[i] == str[k]) dp[i][j] = min( dp[i][j] , dp[i+1][k-1] + dp[k][j])

那么剩下的就类似石子归并的区间DP了,程序如下:

for(int len = 2;len <= n;len ++)  //首先我们先求出将空白串转换成str2需要的次数,这里实质可以使用石子归并的模板
        {
            for(int i = n-len+1;i >=0 ;i --)//起点
            {
                int j = i+len-1;//终点
                dp[i][j] = dp[i+1][j] + 1;//初始值设置
                for(int k = i+1;k<=j;k ++)//寻找中间位置,只有和开头位置相同的时候,我们就可以优化一次,因为相同的话,我们就可以将其分成两部分!
                    if(str2[i] == str2[k])
                        dp[i][j] = min(dp[i][j],dp[i+1][k-1]+dp[k][j]);
            }
        }

剩下的就是我们的转化过程了,ans[i]:  [0,i]区间 由 str2 转换为 str1 需要的最少次数,那么ans初始化为 ans[i] = dp[0][i]

当str1[i] == str2[i]时,ans[i] = ans[i-1],如果不相等的话,我们就要去进行上面第三步骤更新,具体请看程序:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int MAXN=110;
int dp[MAXN][MAXN];
char str1[MAXN],str2[MAXN];
int ans[MAXN];
int main()
{
    while(scanf("%s%s",str1,str2)==2)
    {
        int n=strlen(str1);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
            for(int j=i;j<n;j++)
                dp[i][j]=j-i+1;//dp[i][j]现在存储的是 i~j 最多变换的次数,也就是他的长度
        for(int len = 2;len <= n;len ++)  //首先我们先求出将空白串转换成str2需要的次数,这里实质可以使用石子归并的模板
        {
            for(int i = n-len+1;i >= 0 ;i --)//起点
            {
                int j = i+len-1;//终点
                dp[i][j] = dp[i+1][j] + 1;//初始值设置
                for(int k = i+1;k<=j;k ++)//寻找中间位置,只有和开头位置相同的时候,我们就可以优化一次,因为相同的话,我们就可以将其分成两部分!
                    if(str2[i] == str2[k])
                        dp[i][j] = min(dp[i][j],dp[i+1][k-1]+dp[k][j]);
            }
        }
        ///ans[i]:0~i   由str2转换为str1需要的最少次数
        ///从str2转换为str1,这里如果对应相等的话,次数就等于前一个,因为这个位置不用重新转换
        for(int i=0;i<n;i++)
        {
            ans[i]=dp[0][i];
            if(str1[i]==str2[i])//当对应的位置相同时
            {
                if(i==0)ans[i]=0;//第一个位置时
                else ans[i]=ans[i-1];//否则等于前面位置转换的步数,如果一直相等就会一直等于前一个
            }
            else
            {
                for(int j=0;j<i;j++)//如果中间的情况有相等的情况下,枚举中间的点,取最小值
                    ans[i]=min(ans[i],ans[j]+dp[j+1][i]);
            }
        }
        printf("%d\n",ans[n-1]);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/li1615882553/article/details/80160290