最小编辑距离
首先我们先了解一下什么是最小编辑距离
假设我们有两个字符串T, S,我们的目标是把T和S变成一样的字符串
那么我们对于两个字符串某个位置的字符有以下操作
1.如果当前位置字符相同,那么什么都不做
2.如果当前位置字符不相同,我们可以删除T中的一个字符
3.如果当前位置字符不相同,我们可以删除T中的一个字符
4.如果当前位置字符不相同,我们可以更改T中的一个字符为S中的字符
5.如果当前位置字符不相同,我们可以更改S中的一个字符为T中的字符
模板
int dp[1005][1005];
int EditDis(string a, string b)
{
int maxnum = max(a.size(), b.size()) + 1;
for(int i=1;i<=a.size()+1;i++)
for(int j=1;j<=b.size()+1;j++)
dp[i][j] = maxnum;
for(int i=1;i<=a.size()+1;i++)
dp[i][0] = i;
for(int j=1;j<=b.size()+1;j++)
dp[0][j] = j;
for(int i=1;i<=a.size()+1;i++)
{
for(int j=1;j<=b.size()+1;j++)
{
int flag;
if(a[i]==b[j])
flag=0;
else
flag=1;
dp[i][j]=min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+flag));
}
}
return dp[a.size()+1][b.size()+1];
}
初始化很常规,模板的难点在于递推式
根据上述几种操作我们有以下的递推式
dp[i-1][j]+1
:删掉字符串a最后一个字符a[i]
dp[i][j-1]+1
:表示给字符串添加b最后一个字符
dp[i-1][j-1]+flag
表示改变,相同则不需操作次数,不同则需要,用flag
记录,flag为0说明什么也不做,如果为1说明要修改字符
有时还会遇到以下两种情况
dp[i-1][j]
:a中前i-1个字符与b中前j个字符的匹配结果
dp[i][j-1]
:a中前i个字符与b中前j-1个字符的匹配结果
加上这两个情况后dp数组更新就变为
dp[i][j]=min(min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+flag)), min(dp[i-1][j], dp[i][j-1]));
例题
例题来源于蓝桥杯官网的练习题
最优包含
题意
我们称一个字符串 S 包含字符串 T 是指 T 是 S 的一个子序列,即可以从字符串 S 中抽出若干个字符,它们按原来的顺序组合成一个新的字符串与 T 完全一样。
给定两个字符串 S 和 T,请问最少修改 S 中的多少个字符,能使 S 包含 T ?
其中:
1
≤
∣
T
∣
≤
∣
S
∣
≤
1000
1 \leq |T| \leq |S| \leq 1000
1≤∣T∣≤∣S∣≤1000
输入输出样例
输入:
ABCDEABCD
XAABZ
输出:
3
代码
#include<cstring>
#include<iostream>
using namespace std;
char a[1001], b[1001];
int dp[1001][1001];
int n, m;
int main() {
cin >> a + 1 >> b + 1;
n = strlen(a + 1);
m = strlen(b + 1);
memset(dp, 0x3f3f3f3f, sizeof dp);//初始化dp数组,无穷大
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
dp[i][0] = 0;
for (int j = 1; j <= m; j++) {
if (a[i] == b[j]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1] + 1);
}
}
}
cout << dp[n][m] << endl;
return 0;
}
这里的做法比较简单,因为这里只是进行修改,并且是用第二个字符串匹配第一个字符串,所以只包含两种情况