实践报告任选一题进行分析。内容包括:
- 实践题目
- 问题描述
- 算法描述
- 算法时间及空间复杂度分析(要有分析过程)
- 心得体会(对本次实践收获及疑惑进行总结)
1.实践题目
7-3 编辑距离问题 (30 分)
2.问题描述
设A和B是2个字符串。要用最少的字符操作将字符串A转换为字符串B。这里所说的字符操作包括 (1)删除一个字符; (2)插入一个字符; (3)将一个字符改为另一个字符。 将字符串A变换为字符串B所用的最少字符操作数称为字符串A到 B的编辑距离,记为d(A,B)。 对于给定的字符串A和字符串B,计算其编辑距离 d(A,B)。
输入格式:
第一行是字符串A,文件的第二行是字符串B。
提示:字符串长度不超过2000个字符。
输出格式:
输出编辑距离d(A,B)
输入样例:
在这里给出一组输入。例如:
fxpimu xwrs
输出样例:
在这里给出相应的输出。例如:
5
3.算法描述
这道题是类似于最长子串匹配的题目,不过要更深入一些,首先令m[a][b]为 字符串s1下标从0到a-1组成的字符串 与 字符串从s2下标0到b-1组成的字符串的最优编辑距离,接着用动态规划的思想分析m[a][b]与它的子问题是什么关系。通过分析,我们能分析出m[a][b]的表达式:
当s1[a-1] = s2[b-1] 当字符串s1[a-1]和s2[b-1]相等时,则不用对末位s1[a-1]和s2[b-1]进行修改操作,此时m[a][b]最优编辑距离 = m[a-1][b-1]
当s1[a-1] != s2[b-1]时, 这时有三种情况
1.删除末位s1[a-1]和s2[b-1],以使之s1末位与或s2末位相等,只进行一次删除操作,操作数+1,此时s1和s2的末尾元素已经相等, 只需要比较s1的前a个元素与s2的前b-1个元素,或者比较s1的前a-1个元素与s2的前b个元素,因此此时m[a][b]最优编辑距离 = m[a-1][b] + 1 或者 m[a][b-1] + 1
2.增添一位到末位s1[a-1]和s2[b-1],使之s1末位与或s2末位相等,只进行一次增添操作,操作数+1,同理,此时m[a][b]最优编辑距离 = m[a][b-1] + 1 或者 m[a-1][b] + 1
3.修改一位末位s1[a-1]和s2[b-1],使之s1末位与或s2末位相等,只进行一次增添操作,操作数+1,此时s1和s2的末尾元素已经相等, 只需要比较s1的前a-1个元素与s2的前b-1个元素,则m[a][b]最优编辑距离 = m[a-1][b-1] + 1
通过分析可以知道m[a][b]与上,左,左上元素有关,因此可以用自上而下,从左到右的填表方式填表,从而列出递推式:
for (int i = 1; i <= b.size(); i++) { for (int j = 1; j <= a.size(); j++) { if (a[j - 1] == b[i - 1]) { m[i][j] = m[i - 1][j - 1]; } else { m[i][j] = 1 + min(min(m[i - 1][j], m[i][j - 1]), m[i - 1][j - 1]); } } }
继续分析发现,这个二维数组m需要初始化,边界是行、列为0的时候,
具体化分析:
此时有s1=“ab" , s2="" , 求m[2][0],当s2为空时, m[s1.length][0] = s1.length,只要把s2进行s1字符串长度次添加操作就能改为s1。
同理有s1="",s2=”abc",求m[0][3],同上分析m[0][3] = 3
归纳总结有:
m[i][0] = i
m[0][j] = j
综上可以写出初始化+递推过程公式:
for (int i = 0; i <= b.size(); i++) { for (int j = 0; j <= a.size(); j++) { if (i == 0) { m[i][j] = j; } else if (j == 0) { m[i][j] = i; } else { if (a[j - 1] == b[i - 1]) { m[i][j] = m[i - 1][j - 1]; } else { m[i][j] = 1 + min(min(m[i - 1][j], m[i][j - 1]), m[i - 1][j - 1]); } } } }
最后,结果就保存在m[][]的最右下角
完整代码
int min(int a, int b) { return a < b ? a : b; } int main(){ string a; string b; cin >> a; cin >> b; //初始化矩阵 int row = 2001; int column = 2001; int **m; m = (int **)malloc(sizeof(int *) * row);//分配指针数组,计算行的大小 for (int i = 0; i < row; i++) { m[i] = (int *)malloc(sizeof(int) * (column));//分配每个指针所指向的数组,再计算列 } for (int i = 0; i <= b.size(); i++) { for (int j = 0; j <= a.size(); j++) { if (i == 0) { m[i][j] = j; } else if (j == 0) { m[i][j] = i; } else { if (a[j - 1] == b[i - 1]) { m[i][j] = m[i - 1][j - 1]; } else { m[i][j] = 1 + min(min(m[i - 1][j], m[i][j - 1]), m[i - 1][j - 1]); } } } } cout << m[b.size()][a.size()]; }
4.算法时间及空间复杂度分析(要有分析过程)
时间复杂度分析:很简单,两层for循环,时间复杂度为O(n^2)
空间复杂度,借助了一个n×n的二维数组,空间复杂度也为O(n^2)
5.心得体会
这是一道很标准的动态规划问题,重点在于列出递归方程式,只要列出了递归公式,动态规划的题目就迎刃而解。