Approach: Use classic DP to compute an (m+1)x(n+1) table where dp[i][j] = min edits to transform s[:i] → t[:j]. Fill base rows/cols for insert/delete, then compute replace/min of three ops. To produce an edit script, backtrack from dp[m][n] to dp[0][0] choosing the operation that led to the cell.python
def levenshtein_with_script(s: str, t: str):
m, n = len(s), len(t)
# dp[i][j] = distance s[:i] -> t[:j]
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
dp[i][0] = i
for j in range(1, n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
cost = 0 if s[i - 1] == t[j - 1] else 1
dp[i][j] = min(
dp[i - 1][j] + 1, # delete s[i-1]
dp[i][j - 1] + 1, # insert t[j-1]
dp[i - 1][j - 1] + cost # replace or match
)
# Backtrack to produce script
i, j = m, n
script = []
while i > 0 or j > 0:
if i > 0 and j > 0 and dp[i][j] == dp[i - 1][j - 1] and s[i - 1] == t[j - 1]:
script.append(f"KEEP '{s[i-1]}'")
i -= 1; j -= 1
elif i > 0 and j > 0 and dp[i][j] == dp[i - 1][j - 1] + 1:
script.append(f"REPLACE '{s[i-1]}' -> '{t[j-1]}'")
i -= 1; j -= 1
elif i > 0 and dp[i][j] == dp[i - 1][j] + 1:
script.append(f"DELETE '{s[i-1]}'")
i -= 1
else:
script.append(f"INSERT '{t[j-1]}'")
j -= 1
script.reverse()
return dp[m][n], script
# Example:
# dist, ops = levenshtein_with_script("kitten", "sitting")
# print(dist) # 3
# print("\n".join(ops))
Key points:- Time: O(m*n) (DP fill). Space: O(m*n) for full table to allow backtracking.- If only distance needed, reduce space to O(min(m,n)) by keeping only two rows (current and previous). That yields same time but linear space.- If you need the edit script with linear space, use Hirschberg's algorithm: divide-and-conquer that computes split points using forward and backward linear-space DP — still O(m*n) time but O(min(m,n)) space.Edge cases: empty strings, identical strings (script will be all KEEP), Unicode characters (works as long as strings iterate by character).