编辑距离

来源:互联网 发布:魔兽争霸改图软件 编辑:程序博客网 时间:2024/05/02 09:19

 做文本分析的时候,我们常常需要计算两篇文本的相关性(相关性除了可以使用相似度还可以使用距离-当然是应该反着来算的),如果你不知道把文本转换为一个向量(如果你知道这个,那遭了,因为那样你就可能认为我这里的很rubbish),那你可能会考虑我们这里介绍的最朴素的方法-计算字符串间编辑距离的方法。

         先给出我遇到的问题(也即定义编辑距离):

是两个字符串。对于字符串可以执行如下操作:

1 删除一个字符;

2)插入一个字符;

3)将一个字符替换成另外一个字符。

例如将kitten一字转成sitting

(1). sitten (ks)

(2). sittin (ei)

(3). sitting (g)

利用上面的三种操作可以将字符串转换成字符串B。这种转换所需要的最少的字符串操作次数称为字符串的编辑距离,记为d(A, B)。请设计一个算法,对任意给定的两个字符串B,计算出他们的编辑距离d(A, B)

         我是从另一面考虑这个问题的!希望计算A变到B的最少步骤,我可以先看看AB之间有多少是相同的,然后把一些不相同的按照相应操作变就是了。这里能这么做是因为所提供的操作允许我们有这样的假设。

         比如,kittensittingittn是相同的(不必须他们连续),那么这些相同的是我们不需要动的,然后就是要把ke变成sig了,这里应为各个规则权重一样,那么就是3了;如果各个规则权重不一样就没那么简单了。

         依据上面的分析,那么我们主要就是要找到两字符串的公共字符串的大小,然后用两字符串的长度的少大值减去它就可以了。也就是:编辑距离=max(字符串1的长度,字符串2的长度)-两字符串的最长公共长度。

         好的,现在重点就是要计算两字符串的最长公共长度。我是这样考虑的,如果要计算str1str2的最长公共长度。我首先从str1的字符从左到右一个个来,假设str1m个字符与str2n个字符的最长公共长度为dist[m][n],那么假设str1m+1个字符与str2n+1个字符的最长公共长度就为以下两种情况了:1str1[m+1]==str2[n+1],这个时候的dist[m+1][n+1]就应该是dist[m][n]+1了;(2) str1[m+1]!=str2[n+1] ,这个时候的dist[m+1][n+1]就应该是dist[m][n+1]dist[m+1][n]中较大点的那个了。好了,就是这样思想,这是一种动态规划的想法,下面给出动态规划状态转移方程:

dist[m][n]=(1):dist[m-1][n-1],str1[m]==str2[n];(2):max(dist[m-1][n],dist[m][n-1]),else

(注,我不会在这里写数学公式)

好的,下面给出代码,因为听报告的时候电脑没安装C语言开发环境,又没网下不了DEV-C++,所以就直接用Java语言写了(其实这种问题用C语言作为描述代码更好,大家委屈下,因为我也懒的再写一个了)

代码1:

//求解两字符串的编辑距离

public static int EditDistance(String str1,String str2)

{

         char[] strs1=str1.toCharArray();//转换为字符数组

         char[] strs2=str2.toCharArray();//转换为字符数组

         int[][] dist=new int[strs1.length+1][strs2.length+1];//定义距离二维数组,多定义一维是为了避免边界检测

         int i,j,temp;

         for(i=0;i<=strs1.length;i++) dist[i][0]=0;//初始化边界值

         for(i=0;i<=strs2.length;i++) dist[0][i]=0;//初始化边界值

         for(i=1;i<=strs1.length;i++)

         {

                   for(j=1;j<=strs2.length;j++)

                   {                         

                            if(strs1[i-1]==strs2[j-1])//如果两字符相等,则取dist[i-1][j-1]+1

                            {

                                     temp=dist[i-1][j-1]+1;

                            }

                            else//不等,则取dist[i-1][j]与dist[i][j-1]的最大值

                            {

                                     temp=dist[i][j-1];

                                     if(dist[i-1][j]>temp) temp=dist[i-1][j];

                            }

                            dist[i][j]=temp;

                   }

         }

         temp=dist[strs1.length][strs2.length];

         if(strs1.length>strs2.length)

                   return strs1.length-temp;

         else

                   return strs2.length-temp;

}

 

对这个程序作出时空分析的时候发现,时间复杂度为O(mn),空间复杂度也为O(mn),有没有改进的余地呢?其实还是有的。注意到,每次状态转移的时候,我们只需考虑dist[i-1][*]这一维数组和dist[i][*]这一维,所以我们时候只用两个一维数组就可以了呢,然后使用两个数组一次轮倒(轮倒是我创造的词,意思就是轮流的倒来倒去),其实都不用轮倒,只要每次把[i][*]的结果用[i-1][*]保存就好了!

代码二:

//求解两字符串的编辑距离的优化算法,旨在解决其空间复杂度过高(达到0(m*n))的情况,优化后空间复杂度为o(n)

public static int EditDistanceUp(String str1,String str2)

{

         char[] strs1=str1.toCharArray();//转换为字符数组

         char[] strs2=str2.toCharArray();//转换为字符数组

         int[][] dist=new int[2][strs2.length+1];//定义距离二维数组,多定义一维是为了避免边界检测,

         //每次都在dist[0][*]与dist[1][*]之间倒

         //dist[0][*]存储上次最优结果,也就是dist[i-1][j]

         //dist[1][*]存储这次最优结果,也就是dist[i][j]

         int i,j,temp;

         for(i=0;i<=strs2.length;i++) dist[0][i]=0;//初始化边界值

         dist[1][0]=0;

         for(i=1;i<=strs1.length;i++)

         {

                   for(j=1;j<=strs2.length;j++)

                   {                                   

                            if(strs1[i-1]==strs2[j-1])//如果两字符相等

                            {

                                     temp=dist[0][j-1]+1;

                            }

                            else//不等,则取dist[i-1][j]与dist[i][j-1]的最大值

                            {

                                     temp=dist[1][j-1];

                                     if(dist[0][j]>temp) temp=dist[0][j];

                            }

                            dist[1][j]=temp;

                   }

                   for(j=0;j<=strs2.length;j++)

                            dist[0][j]=dist[1][j];

         }

         temp=dist[1][strs2.length];

         if(strs1.length>strs2.length)

                   return strs1.length-temp;

         else

                   return strs2.length-temp;

}

 

暂时就想到这么多了,应该还有改进的余地!如你有好方法解决此类问题,还忘不吝赐教。

PS:祝元旦快乐!一年更比一年好!