针对字符串定义三种操作: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");
}
}