用动态规划求解最小编辑距离

针对字符串定义三种操作:1,删除某个字符 2,更改某个字符3,在某个位置插入某个字符。

定义最小编辑距离d(i,j):在只允许对字符串做上述三种操作的情况下,把字符串P[1,….,i]变成S[1,….,j]所需要的最小操作次数。


从问题的描述上来看,此问题可以用动态规划来解。

1.d(i,j)取到最小能保证d(m,k)最小(这里m<i,k<j)

证明:若d(i,j)是全局最优,假设d(m,k)可以取更小的值,显然我们可以通过递推关系式来递推求解d(i,j),此时的d(i,j)会更小,不符合之前d(i,j)是全局最优的假设,故d(i,j)最小时必然包含了d(m,k)最小

2.递推求解d(i,j)会反复调用小规模子问题            

 

针对字符串P[1,….,i],S[1,….,j] 假设i<=j(这个假设不影响结果)

如果Pi=Sj  显然d(i,j)=d(i-1,j-1)

如果Pi!=Sj  我们可以做的操作是:

1. 把Sj 插入到P[1,….,i]后面,此时d(i,j)=d(i,j-1)+1(因为此时P[1,….,i]变成了[P[1,….,i],Sj],进行了一次插入操作,其最后一个字符也已经和字符串S[1,….,j]的最后一个字符Sj 匹配上了)

2. 把P[1,….,i]的最后一个字符删除掉,此时d(i,j)=d(i-1,j)+1

3. 把P[1,….,i]的最后一个字符改成Sj ,此时d(i,j)=d(i-1,j-1)+1 (此时P[1,….,i]变成了[P[1,….,i-1],Sj],进行了一次删除操作,其最后一个字符也已经和字符串S[1,….,j]的最后一个字符Sj 匹配上了)

 

在进行字符匹配时,可以定义一个效用函数来刻画Pi=Sj 对匹配结果的影响

 如果Pi=Sj  cost=0否则cost=1 

所以从上述分析可以得知:d(i,j)=min{ d(i,j-1)+1, d(i-1,j)+1, d(i-1,j-1)+cost}


动态规划采用的解决问题的方法是自底向上的,也就是先把小规模问题解决好,把小规模问题的解集合起来就可以解决大规模的问题.

显然有:i=0或者j=0表示P[1,….,i]或者S[1,….,j] 为空,故有 d(0,0)=0;d(i,0)=i;d(0,j)=j;有了这些小规模问题的解,我们就可以开始解决问题了。

 程序版本1:单纯返回d[i][j],此版本注意释放动态内存

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int min(int a,int b,int c)
{
	int temp=a<b?a:b;
	return temp<c?temp:c;
}
int getminedit(char * str1,char * str2)
{
	int m=strlen(str1);  
	int n=strlen(str2);
    int cost;
	int **d=(int **)malloc((m+1)*sizeof(int*));//分配内存空间
    int i,j; 
	int temp;
	for(i=0;i<=m;i++)
	{
		d[i]=(int*)malloc((n+1)*sizeof(int));// 分配内存空间
	}
	for(i=0;i<=m;i++)
	{
		for(j=0;j<=n;j++)
		{
          if(i==0) d[i][j]=j;//i==0,j==0分别表示空字符串
		  else if(j==0)
			  d[i][j]=i;
		  else
		  {
			  if(str1[i]==str2[j])
				  cost=0;
			  else 
				  cost=1;
			  d[i][j]=min(d[i-1][j-1]+cost,d[i-1][j]+1,d[i][j-1]+1);
			 temp=d[i][j];
		  }
		}
		 
}
free(d);
return temp;
//	return d;
}

void main()
{
char *a="abs";
char *b="abcc";
int d=getminedit(a, b);
printf("%d\n",d);
}

程序版本二:返回d数组,不能释放动态内存

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int min(int a,int b,int c)
{
	int temp=a<b?a:b;
	return temp<c?temp:c;
}
int** getminedit(char * str1,char * str2)
{
	int m=strlen(str1);  
	int n=strlen(str2);
    int cost;
	int **d=(int **)malloc((m+1)*sizeof(int*));//分配内存空间
    int i,j; 
	int temp;
	for(i=0;i<=m;i++)
	{
		d[i]=(int*)malloc((n+1)*sizeof(int));// 分配内存空间
	}
	for(i=0;i<=m;i++)
	{
		for(j=0;j<=n;j++)
		{
          if(i==0) d[i][j]=j;//i==0,j==0分别表示空字符串
		  else if(j==0)
			  d[i][j]=i;
		  else
		  {
			  if(str1[i]==str2[j])
				  cost=0;
			  else 
				  cost=1;
			  d[i][j]=min(d[i-1][j-1]+cost,d[i-1][j]+1,d[i][j-1]+1);
			 temp=d[i][j];
		  }
		}
		 
}
//free(d);
//return temp;
	return d;
}

void main()
{
char *a="abs";
char *b="abcc";
//int d=getminedit(a, b);
//printf("%d\n",d);
int **d=getminedit(a, b);
int i,j;
for(i=0;i<=strlen(a);i++)
{
for(j=0;j<strlen(b);j++)
printf("%d,",d[i][j]);
printf("\n");
}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值