算法导论·动态规划·编辑距离

 

算法导论·动态规划·编辑距离

原书习题 15-5 第一部分

#encoding:utf-8

from collections import namedtuple

x = 'algorisssssthmssss'
y = 'altroidddthm'
z = [''] * len(y)
costs = namedtuple('Costs',['copy','replace','delete','insert','twiddle','kill'])(0,1,2,2,1,0)

m = len(x)
n = len(y)

i = j = 0
total_costs = 0

def copy():
	global x,z,i,j,costs,total_costs
	z[j] = x[i]
	i += 1
	j += 1
	total_costs += costs.copy

def undo_copy():
	global i,j,z,costs,total_costs
	j -= 1
	i -= 1
	z[j] = ''
	total_costs -= costs.copy

def replace():
	global x,z,i,j,costs,total_costs
	z[j] = y[j]
	i += 1
	j += 1
	total_costs += costs.replace

def undo_replace():
	global i,j,z,costs,total_costs
	j -= 1
	i -= 1
	z[j] = ''
	total_costs -= costs.replace

def delete():
	global x,z,i,j,costs,total_costs	
	i += 1
	total_costs += costs.delete

def undo_delete():
	global i,costs,total_costs
	i -= 1
	total_costs -= costs.delete

def insert():
	global x,z,i,j,costs,total_costs
	z[j] = y[j]
	j += 1
	total_costs += costs.insert

def undo_insert():
	global j,z,costs,total_costs
	j -= 1
	z[j] = ''
	total_costs -= costs.insert
 
def twiddle():
	global x,z,i,j,costs,total_costs
	z[j] = x[i+1]
	z[j+1] = x[i]
	i += 2
	j += 2
	total_costs += costs.twiddle

def undo_twiddle():
	global i,j,z,costs,total_costs
	j -= 2
	i -= 2
	z[j+1] = ''
	z[j] = ''
	total_costs -= costs.twiddle

def kill():
	global x,z,i,j,costs,total_costs,m
	i = m + 1
	total_costs += costs.kill

def undo_kill(i_):
	global i,costs,total_costs
	i = i_
	total_costs -= costs.kill

def finish():
	global x,y,z,i,j,costs,total_costs
	if ''.join(z) == y:
		print ('OK!')
	else:
		print('ERROR:\n\ty:{0}\n\tz:{1}'.format(','.join(y),','.join(z)))

def memoization():
	global x,y,z,i,j,m,n,costs,total_costs

	memo = {}

	def get_minimal_cost():
		# save values
		# try each operation
			# do operation
			# save minimal value
			# undo
		global x,y,z,i,j,m,n,costs,total_costs
		if (i,j) in memo: return memo[(i,j)]

		# i_,j_,total_costs_ = i,j,total_costs
		min_cost = float('inf')
		min_operations = []

		if j<n and i<m and y[j]==x[i]:
			copy()
			sub_cost,sub_op = get_minimal_cost()
			cost = costs.copy + sub_cost
			if min_cost > cost:
				min_cost = cost
				min_operation = ['copy'] + sub_op
			undo_copy()

		if j<n and i<m:
			replace()
			sub_cost,sub_op = get_minimal_cost()
			cost = costs.replace + sub_cost
			if min_cost > cost:
				min_cost = cost
				min_operation = ['replace'] + sub_op
			undo_replace()

		if i<m:
			delete()
			sub_cost,sub_op = get_minimal_cost()
			cost = costs.delete + sub_cost
			if min_cost > cost:
				min_cost = cost
				min_operation = ['delete'] + sub_op
			undo_delete()

		if j<n:
			insert()
			sub_cost,sub_op = get_minimal_cost()
			cost = costs.insert + sub_cost
			if min_cost > cost:
				min_cost = cost
				min_operation = ['insert'] + sub_op
			undo_insert()

		if i < m-1 and j < n - 1 and x[i] == y[j+1] and y[j] == x[i+1]:
			twiddle()
			sub_cost,sub_op = get_minimal_cost()
			cost = costs.twiddle + sub_cost
			if min_cost > cost:
				min_cost = cost
				min_operation = ['twiddle'] + sub_op
			undo_twiddle()

		if i>=m and j>=n:
			i_ = i
			kill()
			cost = costs.kill
			if min_cost > cost:
				min_cost = cost
				min_operation = ['kill']
			undo_kill(i_)

		# min_operation()
		memo[(i,j)] = (min_cost,min_operation)
		return min_cost,min_operation

	min_cost,operations = get_minimal_cost()
	print(min_cost)
	for op in operations:
		eval('{0}()'.format(op))
		print(op,'->',''.join(z))
	finish()

def dynamic_prog():	
	global x,y,z,m,n,costs

	memo = {}#(i,j)->(cost,op,next_node)
	memo[(m,n)] = (0,'finish',None)

	EMPTY = (0,'',None)
	MAX = (float('inf'),'',None)
	
	def get_memo(i_,j_):
		if i_>m or j_>n:
			return MAX
		return memo[(i_,j_)]

	# while i>=0 and j >= 0:		
	for i_ in range(m,-1,-1):
		for j_ in range(n,-1,-1):
			print(i_,j_)
			if i_ == m and j_ == n:
				continue

			min_cost = MAX
			if i_ < m and j_ < n and x[i_] == y[j_]: 
				c = costs.copy + get_memo(i_+1,j_+1)[0]
				if c<min_cost[0]:
					min_cost = (c,'copy',(i_+1,j_+1))

			if i_ < m and j_ < n:
				c = costs.replace + get_memo(i_+1,j_+1)[0]
				if c<min_cost[0]:
					min_cost = (c,'replace',(i_+1,j_+1))

			if j_ < n:
				c = costs.insert + get_memo(i_,j_+1)[0]
				if c<min_cost[0]:
					min_cost = (c,'insert',(i_,j_+1))

			if i_ < m:
				c = costs.delete + get_memo(i_+1,j_)[0]
				if c<min_cost[0]:
					min_cost = (c,'delete',(i_+1,j_))

			if i_ < m - 1 and j_ < n - 1 and x[i_] == y[j_+1] and x[i_+1] == y[j_]:
				c = costs.twiddle + get_memo(i_+2,j_+2)[0]
				if c<min_cost[0]:
					min_cost = (c,'twiddle',(i_+2,j_+2))

			if i_ >= n:
				c = costs.kill
				if c<min_cost[0]:
					min_cost = (c,'kill',(m,n))

			memo[i_,j_] = min_cost

	#output the result
	p = get_memo(0,0)
	while p[2] != None:
		eval('{}()'.format(p[1]))
		print(p,''.join(z))
		p = get_memo(*p[2])

	finish()

if __name__ == '__main__':
	# memoization()
	dynamic_prog()