算法导论15.5 动态规划——编辑距离问题 C++实现

本文介绍如何使用自底向上的动态规划方法解决文本串的转换问题,通过最少操作将一个字符串x转换为另一个字符串y,涉及复制、替换、删除、插入、旋转和终止等六种操作。通过实例演示和代价计算,展示了如何找到最优化的操作序列及其成本。
摘要由CSDN通过智能技术生成

问题描述

为了将一个文本串 x [ 1 ⋯ m ] x\begin{bmatrix}1 \cdots m \\\end{bmatrix} x[1m]转换为目标串 y [ 1 ⋯ n ] y\begin{bmatrix}1 \cdots n \\\end{bmatrix} y[1n],我们可以使用多种变换操作。我们的目标是,给定 x x x y y y,求将 x x x转换为 y y y的一个变换操作序列。我们使用一个数组 z z z保存中间结果,假定它足够大,可存下中间结果的所有字符。初始时,&z&是空的;结束时,应有 z [ j ] = y [ j ] z [ j ] = y [ j ] z[j]=y[j] j = 1 , 2 , … , n j = 1 , 2 , … , n j=1,2,,n。我们维护两个下标 i i i j j j,分别指向 x x x y y y中的位置,变换操作允许改变 z z z的内容和这两个下标。初始时, i = j = 1 i = j = 1 i=j=1。在转换过程中应处理 x x x的所有字符,这意味着在变换操作结束时,应当有 i = m + 1 i = m + 1 i=m+1
我们可以使用如下6种变换操作:
复制(copy) — 从 x x x复制当前字符到 z z z,即进行赋值 z [ j ] = x [ i ] z [ j ] = x [ i ] z[j]=x[i],并将两个下标 i i i j j j都增1。此操作处理了 x [ i ] x[i] x[i]
  替换(replace) — 将 x x x的当前字符 x [ i ] x[i] x[i]替换为另一个字符 c c c,然后进行赋值 z [ j ] = c z [ j ] = c z[j]=c,并将两个下标i ii和j jj都增1。此操作处理了 x [ i ] x [ i ] x[i]
  删除(delete) — 删除 x x x中的当前字符 x [ i ] x[i] x[i],即将 i i i增1, j j j不变。此操作处理了 x [ i ] x[i] x[i]
  插入(insert) — 将字符 c c c插入 z z z中, z [ j ] = c z[j]=c z[j]=c,将 j j j增1, i i i不变。此操作未处理 x x x中的字符。
  旋转(twiddle,即交换) — 将 x x x中的当前字符和下一个字符复制到 z z z中,但交换顺序, z [ j ] = x [ i + 1 ] z [ j ] = x [ i + 1 ] z[j]=x[i+1] z [ j + 1 ] = x [ i ] z [ j + 1 ] = x [ i ] z[j+1]=x[i],将 i i i j j j都增2。此操作处理了 x [ i ] x[i] x[i] x [ i + 1 ] x[i+1] x[i+1]
  终止(kill) — 删除 x x x中的剩余字符,令 i = m + 1 i=m+1 i=m+1。此操作处理了 x x x中所有尚未处理的字符。如果执行此操作,则转换过程结束。
  每个变换操作都有相应的代价。具体的代价依赖于特定的应用,但我们假定每个操作的代价是一个已知的常量。我们还假定复制和替换的代价小于删除和插入的组合代价,否则复制和替换操作就没有意义了。一个给定的变换操作序列的代价为其中所有变换操作的代价之和。

算法实现

使用自底向上的动态规划来解决这个问题,最优的代价由前序代价+操作代价得出。

//author:Yiwantang
//EditionDistance.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <string>
#include <vector>
#include <limits.h>


void Print_Operator_Chain(std::vector<std::vector<int> > op ,int i, int j,int p)
{
  if ((i == 0 && j == 0)||i<0||j<0)
  {
  	return;
  }
  else if (op[i][j] == 0)
  {
  	Print_Operator_Chain(op, i-1, j-1,p);
  	std::cout << "Copy " << i << " to " << j << std::endl;
  }
  else if (op[i][j] == 1)
  {
  	Print_Operator_Chain(op, i-1, j-1,p);
  	std::cout << "Replace " << i << " to " << j << std::endl;
  }
  else if (op[i][j] == 2)
  {
  	Print_Operator_Chain(op, i-1, j,p);
  	std::cout << "Delete " << i << " to " << j << std::endl;
  }
  else if (op[i][j] == 3)
  {
  	Print_Operator_Chain(op, i, j-1,p);
  	std::cout << "Insert " << i << " to " << j << std::endl;
  }
  else if (op[i][j] == 4)
  {
  	Print_Operator_Chain(op, i-2, j-2,p);
  	std::cout << "Twiddle " << i << " to " << j << std::endl;
  }
  else if (op[i][j] == 5)
  {
  	Print_Operator_Chain(op, p, j,p);
  	std::cout << "kill " << p << std::endl;
  }
  else
  {
  	return;
  }
}

int Edit_Distance()
{
  using std::string;
  int cost[6] = { 0,1,2,2,0,0 };
  int d[6];
  int p;
  std::cout << "Please input two strings:" << std::endl;
  string x, y;
  std::cin >> x >> y;
  int x_length = x.length();
  int y_length = y.length();
  using std::vector;
  vector<vector<int> > c(x_length + 1, vector<int>(y_length + 1, 0));
  vector<vector<int> > op(x_length + 1, vector<int>(y_length + 1, 0));
  for (int i = 0; i <= x_length; i++)
  {
  	c[i][0] = i;
  	op[i][0] = 2;
  }
  for (int i = 0; i <= y_length; i++)
  {
  	c[0][i] = i;
  	op[0][i] = 3;
  }
  if (y_length == 0 && (cost[5] < c[x_length][0]))
  {
  	c[x_length][0] = cost[5];
  	op[x_length][0] = 5;
  	p = 0;

  }
  for (int i = 1; i <= x_length; i++)
  	for (int j = 1; j <= y_length; j++)
  	{
  		for (int k = 0; k < 6; k++)
  			d[k] = INT_MAX;
  		if (x[i-1] == y[j-1])
  		{
  			d[0] = cost[0] + c[i - 1][j - 1];
  		}
  		else
  		{
  			d[1] = cost[1] + c[i - 1][j - 1];
  		}
  		d[2] = cost[2] + c[i - 1][j];
  		d[3] = cost[3] + c[i][j - 1];
  		if (i >= 2 && j >= 2 && x[i - 2] == y[j-1] && x[i-1] == y[j - 2])
  			d[4] = cost[4] + c[i - 2][j - 2];
  		if (i == x_length && j == y_length)
  		{
  			for (int k = 0; k < x_length; k++)
  				if (cost[5] + c[k][y_length] < d[5])
  				{
  					d[5] = cost[5] + c[k][y_length];
  					p = k;
  				}
  		}
  		c[i][j] = INT_MAX;
  		for (int k = 0; k < 6; k++)
  		{
  			if (d[k] < c[i][j])
  			{
  				c[i][j] = d[k];
  				op[i][j] = k;
  			}
  		}
  	}
  for (int i = 0; i <= x_length; i++) {
  	for (int j = 0; j <= y_length; j++)
  	{
  		std::cout << c[i][j] << " ";
  	}
  	std::cout << std::endl;
  }
  Print_Operator_Chain(op, x_length, y_length, p);
  return 0;
}

int main()
{
  Edit_Distance();
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值