hdoj 2476 String painter

原题:

 There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations? 

Input
Input contains multiple cases. Each case consists of two lines:
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
Output
A single line contains one integer representing the answer.

Sample Input

zzzzzfzzzzz
abcdefedcba
abababababab
cdcdcdcdcdcd

Sample Output

6
7

中文:
给你两个字符串a和字符串b,你有一个刷子,一次可以将一个连续区间的字符串刷成同一种字符,限制问你最少刷多少次可以将字符串a刷成b。
例如第一个给的样例
zzzzzfzzzzz
abcdefedcba
要求将字符串zzzzzfzzzzz刷成abcdefedcba
第一步可以刷成这样
aaaaaaaaaaa
第二步
abbbbbbbbba
第三步
abcccccccba
第四步
abcdddddcba
第五步
abcdeeedcba
第六步
abcdefedcba

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 10;


char s1[maxn],s2[maxn];
int dp[maxn][maxn];
int ans[maxn];

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>&s1[1])
    {
        cin>>&s2[1];
        memset(dp,0,sizeof(dp));
        memset(ans,0,sizeof(ans));
        int len = strlen(&s1[1]);

        for(int R = 1;R<=len ;R++)
        {
            for(int L = R; L>=1; L--)
            {
                dp[L][R] = dp[L+1][R]+1;
                for(int k = L + 1; k<=R ;k++)
                {
                    if(s2[L] == s2[k])
                        dp[L][R] = min(dp[L][R], dp[L+1][k] + dp[k+1][R]);
                }
            }
        }
        for(int i = 1;i<=len;i++)
        {
            ans[i]=dp[1][i];
        }
        for(int i = 1;i<=len;i++)
        {
            if(s1[i] == s2[i])
                ans[i] = ans[i-1];
            else
            {
                for(int j=1;j<=i;j++)
                    ans[i]=min(ans[i],dp[j+1][i] + ans[j]);
            }
        }
        cout<<ans[len]<<endl;
    }
    return 0;
}


解答:

数据上来说,字符串长度最多只有100。这意味着要求时间复杂度要在O(n^3)以内完成。
按照给的样例,手动模拟一下不难发现,如果单纯利用区间dp的模型来考虑,每次进行状态转移的的方法按照区间来考虑,状态转移方程如下:
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ x − 1 ] + p a i n t ( x , y ) + d p [ y + 1 ] [ j ] ) dp[i][j] = min(dp[i][j],dp[i][x-1] + paint(x,y) + dp[y+1][j] ) dp[i][j]=min(dp[i][j],dp[i][x1]+paint(x,y)+dp[y+1][j])
其中paint(x,y)表示把a字符串涂成b字符串,这里有个问题,paint(x,y)函数怎么实现?
如果要将区间[x,y]涂成字符串b对应的字符串,那么如何处理这个区间才能使paint函数最优?
这样又回到了原来的问题。
所以,此题不好直接在区间上考虑。

考虑将一个空字符串涂抹成字符串b所需要的最少涂抹次数,设置状态dp[i][j]表示将空字符串区间[i,j]涂抹成字符串b区间[i,j]的字符串所需要的最少涂抹次数:

状态转移方程表示为:
初始 d p [ i ] [ j ] = d p [ i + 1 ] [ j ] + 1 dp[i][j]= dp[i+1][j] + 1 dp[i][j]=dp[i+1][j]+1表示可以将第i个字符单独涂成字符串b[i]对应的字符,剩下的[i+1,j]的字符使用dp[i+1][j]的方式涂上。

如果在字符串b中有b[i]==b[k],其中k∈[i+1,j]
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i + 1 ] [ k ] + d p [ k + 1 ] [ j ] ) dp[i][j] = min(dp[i][j],dp[i+1][k] + dp[k+1][j]) dp[i][j]=min(dp[i][j]dp[i+1][k]+dp[k+1][j])
表示如果b[i]字符与b[k]字符相同,那么可以先将区间[i,k]一次涂抹成字符b[i]的形式,再对[i+1,k]的字符进行操作

看了几个博客,下面有一些评论:

如果在 b [ i ] = = b [ k ] b[i]==b[k] b[i]==b[k] d p [ i + 1 ] [ k ] dp[i+1][k] dp[i+1][k]表示将区间[i,k]涂抹成字符串b[k],为什么不可以表达成下面这样的形式?
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i + 1 ] [ k ] + 1 + d p [ k + 1 ] [ j ] ) dp[i][j] = min(dp[i][j],dp[i+1][k]+1 + dp[k+1][j]) dp[i][j]=min(dp[i][j]dp[i+1][k]+1+dp[k+1][j])

d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i + 1 ] [ k − 1 ] + 1 + d p [ k + 1 ] [ j ] ) dp[i][j] = min(dp[i][j],dp[i+1][k-1] + 1 + dp[k+1][j]) dp[i][j]=min(dp[i][j]dp[i+1][k1]+1+dp[k+1][j])

第一个状态转移方程不能+1的原因在于,如果 b [ i ] = = b [ k ] b[i]==b[k] b[i]==b[k]那么你要涂抹区间[i,k]时,也就是再 d p [ i ] [ k ] dp[i][k] dp[i][k]这个状态的中,以及涂抹成了b[i]了(加过1了),如果再加1,那就重复。

第二个方程不能写成 d p [ i + 1 ] [ k − 1 ] + 1 dp[i+1][k-1] + 1 dp[i+1][k1]+1形式,因为是第b[i]与b[k]相同,表示字符串[i,k]已经涂抹成了b[i]或b[k]表示的状态, d p [ i + 1 ] [ k ] dp[i+1][k] dp[i+1][k],表示你刷了第k个字符。

可以画个3*3的表,计算一下aba这个数据

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值