字符串编辑距离: 是一种字符串之间相似度计算的方法。给定两个字符串S、T,将S转换成T所需要的删除,插入,替换操作的数量就叫做S到T的编辑路径。而最短的编辑路径就叫做字符串S和T的编辑距离。
关键在于找到该问题的子问题。
举个例子:S=“eeba” T="abac" 我们可以按照这样的步骤转变:(1) 将S中的第一个e变成a;(2) 删除S中的第二个e;(3)在S中最后添加一个c; 那么S到T的编辑路径就等于3。当然,这种变换并不是唯一的,但如果3是所有变换中最小值的话。那么我们就可以说S和T的编辑距离等于3了。
我们对最后一次的操作分析:
1.最后一次操作为copy。此时,根据题目的定义可知x[i]=y[j],我们待解决的问题就转化成了求将Xi-1转换为Yj-1的最小开销。将Xi-1转换为Yj-1的最优解一定包含在Xi转换为Yj的最优解内。用反证法证明:若存在从Xi-1到yj-1转换的更小的开销,那么用这个更小的开销来代替Xi-1转换为yj-1的最优解,这样就会得到一个更小的从Xi转换为Yj的开销,与已知的Xi转换为Yj的最优解矛盾,所以假设不成立。因此,当最后一次操作为copy时,可以定义c[i,j]=c[i-1,j-1]+cost(copy)。
2.最后一次操作为replace。此时,根据题目的定义可知x[i]≠y[j]。仿照上述分析,可以得到相同的最优子结构。此时c[i,j]=c[i-1,j-1]+cost(replace)。
3.最后一次操作为delete。根据题意,这时只是将x[i]从序列Xi中除去,对序列Yj没有任何影响,此时问题的最优子结构的形式为将Xi-1转换成Yj,于是可以得到c[i,j]=c[i-1,j]+cost(delete)。
4.最后一次操作为insert。根据题意,在进行插入操作时,序列Xi无任何变化,序列Yj添加一个字符,因此,这时候问题的最优子结构的形式为将Xi转换成为Yj-1,此时c[i,j]=c[i,j-1]+cost(insert)。
c语言代码(递归实现和动态规划实现):
// 动态规划实现 最小编辑距离
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// 测试字符串
#define STRING_X "SUNDAY"
#define STRING_Y "SATURDAY"
#define SENTINEL (-1)
#define EDIT_COST (1)
inline
int min(int a, int b) {
return a < b ? a : b;
}
// Returns Minimum among a, b, c
int Minimum(int a, int b, int c)
{
return min(min(a, b), c);
}
// Strings of size m and n are passed.
// Construct the Table for X[0...m, m+1], Y[0...n, n+1]
int EditDistanceDP(char X[], char Y[])
{
// Cost of alignment
int cost = 0;
int leftCell, topCell, cornerCell;
int m = strlen(X)+1;
int n = strlen(Y)+1;
// T[m][n]
int *T = (int *)malloc(m * n * sizeof(int));
// Initialize table
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
*(T + i * n + j) = SENTINEL;
// Set up base cases
// T[i][0] = i
for(int i = 0; i < m; i++)
*(T + i * n) = i;
// T[0][j] = j
for(int j = 0; j < n; j++)
*(T + j) = j;
// Build the T in top-down fashion
for(int i = 1; i < m; i++)
{
for(int j = 1; j < n; j++)
{
// T[i][j-1]
leftCell = *(T + i*n + j-1);
leftCell += EDIT_COST; // deletion
// T[i-1][j]
topCell = *(T + (i-1)*n + j);
topCell += EDIT_COST; // insertion
// Top-left (corner) cell
// T[i-1][j-1]
cornerCell = *(T + (i-1)*n + (j-1) );
// edit[(i-1), (j-1)] = 0 if X[i] == Y[j], 1 otherwise
cornerCell += (X[i-1] != Y[j-1]); // may be replace
// Minimum cost of current cell
// Fill in the next cell T[i][j]
*(T + (i)*n + (j)) = Minimum(leftCell, topCell, cornerCell);
}
}
// 结果存储在 T[m][n]
cost = *(T + m*n - 1);
free(T);
return cost;
}
// 递归方法实现
int EditDistanceRecursion( char *X, char *Y, int m, int n )
{
// 基本情况
if( m == 0 && n == 0 )
return 0;
if( m == 0 )
return n;
if( n == 0 )
return m;
// Recurse
int left = EditDistanceRecursion(X, Y, m-1, n) + 1;
int right = EditDistanceRecursion(X, Y, m, n-1) + 1;
int corner = EditDistanceRecursion(X, Y, m-1, n-1) + (X[m-1] != Y[n-1]);
return Minimum(left, right, corner);
}
int main()
{
char X[] = STRING_X; // vertical
char Y[] = STRING_Y; // horizontal
printf("Minimum edits required to convert %s into %s is %d\n",
X, Y, EditDistanceDP(X, Y) );
printf("Minimum edits required to convert %s into %s is %d by recursion\n",
X, Y, EditDistanceRecursion(X, Y, strlen(X), strlen(Y)));
return 0;
}
以上代码转自点击打开链接
自己的代码:
<span style="font-size:18px;">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int c[1005][1005]={0};
int min(int a,int b)
{
return (a>b)?b:a;
}
int same(char ch2,char ch1)
{
if(ch1==ch2)
return 0;
else
return 1;
}
int main()
{
char a[1005],b[1005];
int lena,lenb,i,j,k;
gets(a);
gets(b);
lena=strlen(a);
lenb=strlen(b);
for(i=0;i<=lena;i++)
c[i][0]=i;
for(j=0;j<=lenb;j++)
c[0][j]=j;
for(i=1;i<=lena;i++)
{
for(j=1;j<=lenb;j++)
{
c[i][j]=min(c[i-1][j-1]+<span style="color:#ff0000;">same(a[i-1],b[j-1])</span>,min(c[i-1][j]+1,c[i][j-1]+1));
}
}
printf("%d",c[lena][lenb]);
return 0;
}
</span>