python数算:动态规划之单词最小编辑问题

 

代码在文末!

因为有学弟问我这个问题,所以来补充几句说明。 可能很多人会找到下面这幅图:

首先要说明我的代码(见本文末尾)是copy加5分,add和delete加20分。但是上图中应该是copy不算分,add和delete算一次编辑。无论怎么赋分,写成代码其实是一样的,只不过分值处改一改而已。此图上方是已有单词,左边是待生成的单词,而数字就是编辑次数(add和delete)。

每次单词转换都是从表格左上方“走”到表格右下方的过程:add是向下一格,delete是向右一格,copy是向右下一格。

其实很好理解:当前格对应的上字母和左字母正是当前正在处理的字母,如果发现相同就copy,原单词和生成单词各进一个字母,转而判断下一个字母的处理。如果不同就可以看看后面是不是有相同的,如果有很多相同的就可以delete掉原单词中间的不同字母或者add上生成单词的中间不同的字母,后面直接copy。

(举个例子,为了使编辑次数最小。当disagree到agree,果断扔掉前三个然后一通copy啊。acdfg转换到accdfg,果断中间add一个c然后copy其余啊。)

当然,情况复杂的时候是不能直接判断该采取什么措施的,这也是为什么需要这个程序来计算各种可能,找到最小的编辑路径。这道题本身完全不难,只要理解怎么在格子间移动就好。比较坑的是很多题目的分值不一样,有时候copy算分有时候不算。虽然本质完全一样,但是初学的时候很容易以为在讲完全一样的一道题,从而产生误解。

再贴几张图:

 

这个就是copy、add、delete都算一次......

 

代码:

class Stack:
    def __init__(self):
        self.items = []
    def isEmpty(self):
        return len(self.items) == 0
    def push(self, item):
        self.items.append(item)
    def pop(self):
        return self.items.pop()
    def peek(self):
        if not self.isEmpty():
            return self.items[len(self.items) - 1]
    def size(self):
        return len(self.items)

def replace(s1,s2,len1,len2):
    mylist = [[0 for j in range(len2+1)] for i in range(len1+1)]#初始化mylist
    mywork = [[None for j in range(len2+1)] for i in range(len1+1)]#初始化mywork
    for i in range(1,len1+1):
        mywork[i][0]="delete"
        mylist[i][0]= 20*i
    for j in range(1,len2+1):
        mywork[0][j]="add"
        mylist[0][j]= 20*j
    #mywork用于储存最佳路径,记录如何到达此位置时
    for i in range(1,len1+1):
        for j in range(1,len2+1):
            copy = mylist[i-1][j-1] + 5#copy相当于向右下移一格
            add = mylist[i][j-1] + 20#add相当于下移一格
            delete = mylist[i-1][j] + 20#delete相当于右移一格
            if s1[i-1] == s2[j-1] and (copy<add and copy<delete):#只有当对应字母相同时才可以copy,字母编号从0开始
                mylist[i][j] = copy
                mywork[i][j] = "copy"
            elif add>delete:
                mylist[i][j] = delete
                mywork[i][j] = "delete"
            else:
                mylist[i][j] = add
                mywork[i][j] = "add"
    print(mylist[len1][len2])
    return mywork

def get_routine(mywork,l1,l2):
    s=Stack()
    while l1>0:
        s.push(mywork[l1][l2])
        if mywork[l1][l2]=="copy":
            l1,l2=l1-1,l2-1
        elif mywork[l1][l2]=="add":
            l2=l2-1
        elif mywork[l1][l2]=="delete":
            l1=l1-1
    while l2>0:#有时可能走到“边界上”,还需要再向上“回溯”
        s.push("add")
        l2=l2-1
    while s.size()>0:
        print(s.pop())

s1=input()
s2=input()
len1 = len(s1)
len2 = len(s2)
get_routine(replace(s1,s2,len1,len2),len1,len2)

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BeerBread134

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值