编辑距离与滚动数组优化 - 二维动态规划模板

题目:编辑距离

思路

显然,定义 \(f[i][j]\) 表示字符串 \(a\) 中前 \(i\) 个字符到 字符串 \(b\) 中前 \(j\) 个字符的编辑距离。

那么对于 \(a_i=b_j\) 时,我们对当前位无需进行任何编辑操作,则 \(f[i][j]=f[i-1][j-1]\)

如果 \(a_i \ne b_j\) ,那么我们就要对当前位进行编辑:

  • 对于修改操作,我们先要保证 字符串 \(a\) 中前 \(i-1\) 个字符 已经编辑到了 字符串 \(b\) 中前 \(j-1\) 个字符,接下来才把 \(a_i\) 修改成 \(b_j\) ,所以转移为 \(f[i][j]=f[i-1][j-1]+1\)
  • 对于插入操作,因为我们是向 \(a_i\) 后面插入一个 \(b_j\) ,所以我们要保证 字符串 \(a\) 中前 \(i\) 个字符 已经编辑到了 字符串 \(b\) 中前 \(j-1\) 个字符,再同时往这两个字符串后插一个 \(b_j\) ,所以转移为 \(f[i][j]=f[i][j-1]+1\)
  • 对于删除操作,由于我们是删除 \(a_i\) 字符,所以我们要保证 字符串 \(a\) 中前 \(i-1\) 个字符 已经编辑到了 字符串 \(b\) 中前 \(j\) 个字符,这样才能删掉 \(a_i\) ,所以方程为 \(f[i][j]=f[i-1][j]+1\)

这三种情况取一个 min 即可。

注意初始化,\(f[0][0]=0,f[i][0]=i,f[0][j]=j\) ,因为分别要进行这么多次的删除和插入。

#include <bits/stdc++.h>
using namespace std;
string a,b;
int f[2005][2005];
int main()
{
	cin>>a>>b;
	memset(f,0x3f,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<=a.length();i++)f[i][0]=i;
	for(int i=1;i<=b.length();i++)f[0][i]=i;
	for(int i=1;i<=a.length();i++)
	{
		for(int j=1;j<=b.length();j++)
		{
			if(a[i-1]==b[j-1])f[i][j]=f[i-1][j-1];
			else f[i][j]=min(f[i][j],min(f[i-1][j-1]+1,min(f[i-1][j]+1,f[i][j-1]+1)));
		}
	}
	cout<<f[a.length()][b.length()];
	return 0;
}

滚动数组优化

观察到,如果我们把 dp 数组看作一个网格,那么某一个格子的状态一定是由它左边、上边、左上角的格子的状态转移过来的。

因此,我们就可以用滚动数组优化。

但这里的滚动数组不能用像背包一样的倒序转移,因为这里的转移会用到同级的状态:格子左侧。因此我们先要更新格子左边,才能更新这个格子。所以得从左到右转移。

但从左到右转移会覆盖掉左上角的内容,因此我们要把左上角的东西单独记录一下,然后在赋初值的时候注意一下 \(f[0]=i\) 即可。

#include <bits/stdc++.h>
using namespace std;
string a,b;
int f[2005];
int main()
{
	cin>>a>>b;
	f[0]=0;
	for(int i=0;i<=b.length();i++)f[i]=i;
	for(int i=1;i<=a.length();i++)
	{
		int zs=f[0];
		f[0]=i;
		for(int j=1;j<=b.length();j++)
		{
			int tmp=zs;
			zs=f[j];
			if(a[i-1]==b[j-1])f[j]=tmp;
			else f[j]=min(tmp+1,min(f[j]+1,f[j-1]+1));
		}
	}
	cout<<f[b.length()];
	return 0;
}
posted @ 2024-07-22 15:20  KS_Fszha  阅读(9)  评论(0编辑  收藏  举报