序列比较:把一个序列变成另一个序列的最少修改步骤


序列比较问题因其在分子生物学上的应用,今年来一起来人们不少的关注。序列比较问题还可以应用于文件比较和版本维护,比如有一个本地文件(或者程序)修改前后的版本,而需要能方便地抽取其中的不同部分。同一程序也可能有多个版本,如果版本之间相差不大,则在存储时只需要存储有差别的部分,而不必存储整个文件。这些情况下可能只用到插入和删除操作,对于其他情况则需要为不同的修改付出不同的代价。

令A=a1a2…an,B=b1b2…bn是两个字符串,其中字符取自一有限集合(如英语字母表)。有三种方法把A变为B:(1)插入,把某个字符插入字符串;(2)删除,从字符串中删除某个字符;(3)替换,把某个字符替换成另一个字符。例如,把abbc变成babb就可以:abbc->bbc->babc->babb,这样总共需要两步修改。


问题:寻求最少的修改步骤。

分析:通常情况下把一个字符串变成另一个字符串往往有多重方法,究竟哪个最佳还难以定论。下面就用归纳法来探索这个问题的解。记前缀子串a1a2…ai(b1b2..bi)为A(i)B(i),问题转换为用最少的修改步骤将A(n)改为B(m)。

归纳:假设已知将A(n-1)改为B(m)的最佳方法(可能有多种不同的最佳方法,这里假设知道其中一种),那么只要把an删除就可以将A(n)改为B(m),这样做是最佳的吗?那么我们还需要知道些什么信息?我们还需要知道把A(n-1)改为B(m-1)的修改次数以及把A(n)改为B(m-1)的修改次数,也就是需要知道对A、B改动最小的条件下构造出修改次数最少的步骤的所有可能性。


记C(i,j)为A(i)变成B(j)的最小代价,那么要关注的就是这个最小代价,而不是改动本身。我们感兴趣的是如何建立C(n,m)和某个C(i,j)之间的关系,这里i、j小于n、m。不难发现有4中可能性对应着3种操作和1种匹配:


删除:将an删除,于是最佳方法就是先把A(n-1)变成B(m),然后再删除这个字符,即C(n,m)=C(n-1,m)+1


插入:如果A到B的最小改动需要插入某个字符与bm匹配,那么就有C(n,m)=C(n,m-1)+1。也就是先把A(n)以最小代价变成B(m-1),然后再插入一个与bm相同的字符。


替换:如果用an替换bm,那么首先要找到A(n-1)到B(m-1)的最小改动,接下来若an不等于bm,还要加一。


匹配:如果an和bm相同,则C(n,m)=C(n-1,m-1)


总结:如何动态生成二维数组,这个二维数组如何寻址?有几种方法为二维数组分配内存?

记C(i,j)= {0(ai=bi), 1(ai ≠bi} 

 C(n,m)=min{ C(n-1,m)+1, C(n,m-1)+1,C(n-1,m-1)+c(n,m)(替换或者匹配)} 

 初始条件C(i,0)=I; C(0,j)=j 

 实现: 

int min(int num1, int num2, int num3) { int less = (num1 < num2) ? num1 : num2; return (less < num3) ? less : num3; } int **arralloc(int row,int column) { if(row < 0 || column < 0) return 0; int **ptr = new int*[row]; //开辟m个int*型的数据 for(int i = 0; i < row; ++i) { ptr[i] = new int[column];//开辟column个int型的数据 memset(ptr[i],0,sizeof(int)*column); } return ptr; } int minimumchanges(const char *str1,const char *str2,int s1len, int s2len) { if(str1 == NULL || str1 == NULL) return -1; int row = strlen(str1); int column = strlen(str2); int **arr = arralloc(row,column); for(int i = 0; i < column; ++i) arr[0][i] = i; //为什么从1开始,因为arr[0][0]已经初始化了 for(int i = 1; i < row; ++i) arr[i][0] = i; for(int i = 1; i<row; ++i) { for(int j = 1; j < column; ++j) { int deletion = arr[i-1][j]+1; int insertion = arr[i][j-1]+1; int substitution; if(str1[i]==str2[j]) substitution = arr[i-1][j-1]; else substitution = arr[i-1][j-1]+1; arr[i][j] = min(deletion,insertion,substitution); } } int minchanges = arr[row-1][column-1]; for(int i = 0; i<row; ++i) delete []arr[i]; delete []arr; return minchanges; } int _tmain(int argc, _TCHAR* argv[]) { minimumchanges("Saturday","Sunday",8,6); return 0; }