题意:给定两个等长字符串a和b,求从a变换到b需要用的最少的步数,变换方式:选取一段连续子串,将其整段字母全变成其他任意字符,计为一此。
题解:dp的题目。直接从a到b变化比较困难?应该,没试过。所以就有了多次dp。先从空串dp出b串,得到dp[i][j],再冲dp[0][i]中dp出结果。其中dp[i][j]表示从空串刷到b串中的子串b[i]~b[j]所需要的最小步数。
分布dp:
1)先从空串到b串。这里的dp只要注意的是要知道减少变换次数的方式只有几个相同的字母可以一次性刷掉。所以就有了if(b[i]==b[j])dp[i][j]=dp[i][j-1];
2)从1)的结果到最终结果。上述求到得dp[i][j]是从空串来的,还要考虑a串中与b串中相同的字母可能不需要刷,从而减少了刷的次数。在这次dp中dp[0][i]就变成了从a串的0~i子串到b串0~i子串需要的最少次数。之后dp[0][n-1]就是结果。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <map>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=1e6;
const int maxn=103;
char a[maxn],b[maxn];
int dp[maxn][maxn];//dp[i][j]表示刷[i,j]段需要的次数
int main()
{
while(scanf("%s",a)!=EOF)
{
scanf("%s",b);
int i,j,k,n,l;
n=strlen(a);
//初始,默认从空串到b串
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(i==j)
dp[i][i]=1;
else
dp[i][j]=INF;
}
}
for(k=2;k<=n;k++)//宽度
{
for(i=0;i<n-k+1;i++)//起始点
{
j=i+k-1;
if(b[i]==b[j])//头尾相同时可以一次刷掉两个位置,刷的次数减少1
{
dp[i][j]=dp[i][j-1];
continue;
}
for(l=i;l<=j;l++)
dp[i][j]=min(dp[i][j],dp[i][l]+dp[l+1][j]);
}
}
/*for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
if(dp[i][j]==INF)printf("0 ");
else printf("%d ",dp[i][j]);
printf("\n");
}*/
for(i=0;i<n;i++)
{
//判断从a串到b串的时候,只有出现相同字符时次数才有可能缩小
//由于dp[0][i-1]<=dp[0][i],因为只有头尾相同的情况,两者才相等
//当a[i]==b[i]时,只用刷dp[0][i-1]段即可,相当与缩短的刷的次数
if(i==0&&a[i]==b[i])dp[0][i]=0;
else if(a[i]==b[i])dp[0][i]=dp[0][i-1];
//前面的处理缩短了次数,后面的刷法也可以相应的缩短
for(j=0;j<i;j++)
dp[0][i]=min(dp[0][i],dp[0][j]+dp[j+1][i]);
}
printf("%d\n",dp[0][n-1]);
}
return 0;
}
/*
aaa
aaa
0
aaa
aab
1
*/