字符串编辑距离与动态规划

blog

算法非原创,只是整理,并尝试说的更完整和易理解。

字符串编辑距离是什么

将一个字符串转换成另一个字符串时需要付出的代价。转换可以采用插入、删除、替换三种编辑方式。比如说把字符串“post”转换成“get”,直观看来它们的编辑距离颇大,并有许多种转换方式。第一种[替换p为g,替换o为e,删除
s]这样它们的转换代价为3。第二种[删除p,删除o,删除s,删除g,删除e]这样它们的转换代价为5。不同的转换方式需要的编辑次数不一样,最少的那个编辑方式所需的次数就是这量字符串的编辑距离。

简单的递归算法

咋一看这个问题似乎挺难,因为对同一个字符串需要选择在不同位置上采用不同的动作才能完成任务,可能会有一种无法下手的感觉。但是用计算机的思维方式来看,我们可以把一个复杂的大问题转换成一系列简单的小问题,有多
简单呢?比如要把“post”转换成“get”,我们可以先考虑如何用最小的代价把“post”转换成“et”,或者是把“ost”转换成“get”z或者是把“ost”转换成“et”,总之是尝试用三种办法(插入、删除、替换)使问题的复杂度降级,也就是用> 一种递归的方式去处理.因为对每一步转换可选择三种不同的方式(插入、删除、替换),所以需要对比每一部的最优方式。为了调试简单所以就用js做示例。

function editDis(str1,str2){
    if(str1.length==0||str2.length==0){
       return Math.abs(str1.length-str2.length)
    }
    if(str1.charAt(0)===str2.charAt(0)){
       return editDis(str1.substr(1,str1.length),str2.substr(1,str2.length))
    }
    var edInsert = editDis(str1,str2.substr(1,str2.length))+1;
    var edDelete = editDis(str1.substr(1,str1.substr.length),str2)+1;
    var edReplace = editDis(str1.substr(1,str1.substr.length),str2.substr(1,str2.length))+1;
    return Math.min(edInsert,edDelete,edReplace);
}

运行结果如下

editDis("post","get")
3

优化的递归算法

可以看到上边的简单算法就可以得出正确的结果了,虽然简单却造成了大量的计算浪费,时间复杂度太高,在很多子串上做了重复计算。优化?最先想到的是什么?做缓存啊,把计算过的结果存在表里下次计算首先查表就可以了。
照我的习惯就直接弄一个map做缓存了,但本文的主题不还有动态规划么,所以考虑用动态规划的思想去解决。动态规划算法通常基于一个递推公式及一个或多个初始状态,当前问题的解由上一子问题的解推出。对我们的问题来说> ,递归仍然是最容易的办法,只不过可以把每一步子问题的解也就是状态存表供可能存在的其它子问题使用,其实对这个问题来说还是缓存啦。。

var editStatusArr = Array(Array())
function editDisWithStatus(str1,str2,x,y){
  if(x==0&&y==0){
      for(i=0;i<=str1.length;i++)
          editStatusArr[i]=Array();
   }
   if(editStatusArr[x]!=undefined&&editStatusArr[x][y]!=undefined){
       console.log(x+"_"+y);
       return editStatusArr[x][y];
   } else {
       var dis = 0;
       if((str1.length)==0){
           dis = str2.length;
       } else if((str2.length)==0){
           dis = str1.length;
       } else {
           if(str1.charAt(0)===str2.charAt(0)){
           dis = editDisWithStatus(str1.substr(1,str1.length),str2.substr(1,str2.length),x+1,y+1);
           } else {
              var edInsert = editDisWithStatus(str1,str2.substr(1,str2.length),x,y+1)+1;
               var edDelete = editDisWithStatus(str1.substr(1,str1.substr.length),str2,x+1,y)+1;
               var edReplace = editDisWithStatus(str1.substr(1,str1.substr.length),str2.substr(1,str2.length),x+1,y+1)+1;
               dis = Math.min(edInsert,edDelete,edReplace);
           }
       }
       editStatusArr[x][y]= dis;
       console.log("x:"+x+" y:"+y+" dis:"+dis);
       return dis;
  }
}

运行结果如下

  editDisWithStatus("post","get",0,0)
  3

递推算法

有了上面递归的实现,我们基本上可以确定递推关系了.呐,就是下面这个样子,首先把其中一个字串的字符拆开排好,然后在下面标上1234,1前面画0,然后在纵向上在1下面画123,左侧表上另一个字串的字符.这样
递推关系的初始态就有了,接下来就是填其它的数字了,要确定一个空位的数字首先看该空位对应的两个字符是否一样,如果一样的话就取该空位左,左上,上,三个方向上的最小值,如果不一样就取三个方向上的最小值加1.嗯
?你不要骗我!这和递归的实现根本不相似.递归是从最复杂的一步开始思考,由繁至简.而递推是从初始态开始的,由简至繁.其实是差不多的.

p o s t
0 1 2 3 4
g 1 1 2 3 4
e 2 2 2 3 4
t 3 3 3 3 3
function editDis(str1,str2){
  var editStatusArr = Array(Array())
    var i=0,j=0;
    for(i=0;i<=str1.length;i++){
        editStatusArr[i]=Array();
    }
    for(j=0;j<=str2.length;j++){
        editStatusArr[0][j]=j;
    }
    for(i=0;i<=str1.length;i++){
        editStatusArr[i][0]=i;
    }

    for(i=1;i<=str1.length;i++){
        for(j=1;j<=str2.length;j++){
            if(str1.charAt(i-1)===str2.charAt(j-1)){
               editStatusArr[i][j]=editStatusArr[i-1][j-1];
           } else {
               editStatusArr[i][j]=Math.min(editStatusArr[i][j-1],editStatusArr[i-1][j],editStatusArr[i-1][j-1])+1;
           }
       }
 }
 return editStatusArr[i-1][j-1];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,205评论 0 4
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,567评论 18 399
  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,256评论 0 18
  • 如果说爱情是一场凿壁偷光,那么幸运的是,透过缝隙,我们依然能够看到彼此的光亮,那虽然不够耀眼,但足够温和到可以支撑...
    拥有人鱼线的Wz阅读 3,208评论 0 1
  • 我们从小就被灌太多什么世界以痛吻我,要我报之以歌的毒鸡汤,被打太多什么扼住命运的咽喉之类的毒鸡血了。讲真,励志穷三...
    公子菲阅读 766评论 0 0