题目链接:点击打开链接
首先考虑这个问题与一般编辑问题的区别,在这个问题中可以在第二个串的任意地方掐掉重新对第一个串进行dp
也就是说,可以将dp[n][j]的状态转移给dp[0][j](n为第一个串的长度)
要求的答案是所有段中编辑距离最大的那段的最小值,也就是说需要记录所有从n转移到0的时候的dp[n][j]的值以用来比较取最大。
显然这么做十分麻烦,所以可以考虑设一个编辑距离上界,只有当不超过这个上界的情况下才可以进行从n到0的转移。
于是就可以省去dp[n][j]的记录而直接默认答案为该上界。
基于这个思想,我们可以二分这个上界来dp,从而找到上界的最小值。
自己写了一份代码,虽然能够AC,却过不了自己出的随机数据,不知道错在哪了,代码如下:
#include<bits/stdc++.h>
#define INF 10000000
using namespace std;
char s1[55],s2[5005];
int dp[55][5005];
int n,m;
bool check(int lmt)
{
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
dp[i][j]=INF;
dp[0][0]=0;
for(int j=0;j<=m;j++)
{
bool flag=false;
for(int i=0;i<=n;i++)
{
int p=dp[i][j];
if(i>0)dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
if(j>0)dp[i][j]=min(dp[i][j],dp[i][j-1]+1);
if(i>0&&j>0)
{
if(s1[i-1]==s2[j-1])dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
else dp[i][j]=min(dp[i][j],dp[i-1][j-1]+1);
}
if(p!=dp[i][j])flag=true;
}
if(dp[n][j]<=lmt&&flag)
{
dp[0][j]=0;
j--;
}
}
return dp[n][m]<=lmt;
}
int main()
{
// freopen("1.txt","r",stdin);
// freopen("2.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s%s",s1,s2);
n=strlen(s1),m=strlen(s2);
int l=0,r=n;
while(r-l)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
}
之后再参考了
这个博客改了自己的代码,于是就能过自己出的数据了。但是并不知道为什么上面那个会错,代码如下:
#include<bits/stdc++.h>
#define INF 10000000
using namespace std;
char s1[55],s2[5005];
int dp[55][5005];
int n,m;
bool check(int lmt)
{
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
dp[i][j]=INF;
dp[0][0]=0;
for(int j=0;j<=m;j++)
{
if(dp[n][j]<=lmt)
dp[0][j]=0;
for(int i=0;i<=n;i++)
{
dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1);
dp[i][j+1]=min(dp[i][j+1],dp[i][j]+1);
if(i<n&&j<m)
{
if(s1[i]==s2[j])dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]);
else dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]+1);
}
}
}
return dp[n][m]<=lmt;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s%s",s1,s2);
n=strlen(s1),m=strlen(s2);
int l=0,r=n;
while(r-l)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
}