LC 2977. 转换字符串的最小成本 II
https://leetcode.cn/problems/minimum-cost-to-convert-string-ii/
这题也有一个简单的版本,但是这题简单版本转换是单字符,可以对单字符进行更改,相对就简单多了。这题是可以针对字符串进行更改,但是存在两个限制,就是修改的部分不能重合,就让这个问题简单了不少。并且 \(source.length == target.length <= 1000\), 所以这题可以使用O(n^2) 的dp算法来计算。
这题在实现的时候有两个注意点:
- 计算最短路的时候,我们只需要考虑 \(dict[i].length == dict[j].length\) 的转换,因为长度不同不能转换。这样使得在实际运行的时候不会使用到O(n^3)的floyd算法。虽然 \(cost.length == original.length == changed.length <= 100\), 理论上如果两者没有重合的话,那么总数可以到200. 那么O(n^3) 时间就会稍微有点长。
- 进行字符串匹配的时候,因为是在一个点上不断扩展字符串的,所以可以使用trie来匹配而不是每次都计算hash去字典里面查找。
class Solution:
def minimumCost(self, source: str, target: str, original: List[str], changed: List[str], cost: List[int]) -> int:
dictIndex = {}
dictData = []
for s in original + changed:
if s not in dictIndex:
dictIndex[s] = len(dictData)
dictData.append(s)
INF = 1 << 63
n = len(dictData)
C = [[INF] * n for _ in range(n)]
for i in range(len(original)):
a, b, c = original[i], changed[i], cost[i]
a, b = dictIndex[a], dictIndex[b]
C[a][b] = min(C[a][b], c)
for t in range(n):
opts = []
for i in range(n):
if len(dictData[i]) == len(dictData[t]):
opts.append(i)
for i in opts:
for j in opts:
C[i][j] = min(C[i][j], C[i][t] + C[t][j])
class Trie:
def __init__(self):
self.child = [None] * 26
self.index = -1
def insert(self, s, index):
t = self
for c in s:
d = ord(c) - ord('a')
if not t.child[d]:
t2 = Trie()
t.child[d] = t2
t = t.child[d]
t.index = index
def move(self, c):
d = ord(c) - ord('a')
return self.child[d]
root = Trie()
for i in range(len(dictData)):
s = dictData[i]
root.insert(s, i)
N = len(source)
dp = [0] * (N + 1)
for i in reversed(range(N)):
same = True
r = INF
a, b = root, root
for j in range(i, N):
same = same & (source[j] == target[j])
if a and b:
a, b = a.move(source[j]), b.move(target[j])
if same:
r = min(r, dp[j + 1])
elif not (a and b):
break
elif a.index != -1 and b.index != -1:
ta, tb = a.index, b.index
c = C[ta][tb]
r = min(r, c + dp[j + 1])
dp[i] = r
if dp[0] == INF:
return -1
return dp[0]