📌  相关文章
📜  通过将字符移动到前端或末尾来将给定字符串转换为另一个字符串的最少操作(1)

📅  最后修改于: 2023-12-03 15:12:24.756000             🧑  作者: Mango

将给定字符串转换为另一个字符串的最少操作

问题描述

给定两个字符串 $s$ 和 $t$,要求通过将字符移动到前端或末尾(不能改变字符顺序),将 $s$ 转换为 $t$,求最少需要进行多少次操作。

解法

这是一道比较典型的字符串匹配问题,可以用动态规划来解决。

定义状态

设 $dp(i,j)$ 表示将 $s[1:i]$ 转换为 $t[1:j]$ 所需的最少操作数。

状态转移

状态转移方程有两种情况:

  1. 当 $s[i]=t[j]$ 时,无需操作,则有 $dp(i,j) = dp(i-1,j-1)$;
  2. 当 $s[i]\neq t[j]$ 时,需要进行调整。可以将 $s[i]$ 移动到前端或末尾。为了使调整次数最少,我们选择移动次数更少的方式。设 $len$ 为 $t$ 中等于 $s[i]$ 的字符所在位置,如果 $s[i]$ 在 $t$ 中有多个出现位置,则令 $len$ 为距离 $i$ 最近的位置。则有转移方程:$$ dp(i,j) = \min{dp(i-1,j),dp(i,j-1)}+1 $$ 其中,$dp(i-1,j)$ 表示将 $s[1:i-1]$ 转换为 $t[1:j]$ 所需的最少操作数,$dp(i,j-1)$ 表示将 $s[1:i]$ 转换为 $t[1:j-1]$ 所需的最少操作数,$+1$ 表示对 $s[i]$ 的操作。

具体解释如下:

  • 如果 $s[i]$ 移动到前端,则有 $dp(i,j) = dp(i-1,j)+1$;
  • 如果 $s[i]$ 移动到末尾,则有 $dp(i,j) = dp(i,j-1)+1$;
  • 如果将 $s[i]$ 移动到 $t$ 中的某个位置,则有 $dp(i,j) = dp(i-1,j-1)+dist$,其中 $dist = |i-len|$。
初始状态
  • 当 $i=0$ 时,表示将一个空字符串转换为 $t[1:j]$,所需操作数为 $j$,因此有 $dp(0,j) = j$;
  • 当 $j=0$ 时,表示将 $s[1:i]$ 转换为一个空字符串,所需操作数为 $i$,因此有 $dp(i,0) = i$;
返回值

最终答案为 $dp(n,m)$,其中 $n$ 和 $m$ 分别为字符串 $s$ 和 $t$ 的长度。

代码实现
def min_op(s: str, t: str) -> int:
    n, m = len(s), len(t)
    dp = [[0] * (m+1) for _ in range(n+1)]
    for i in range(1, n+1):
        dp[i][0] = i
    for j in range(1, m+1):
        dp[0][j] = j
    for i in range(1, n+1):
        for j in range(1, m+1):
            if s[i-1] == t[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                len_s = s[:i].rfind(s[i-1])
                len_t = t[:j].rfind(s[i-1])
                dist = abs(i-1-len_t)
                dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + dist
    return dp[n][m]
时间复杂度

该算法的时间复杂度为 $O(nm)$,其中 $n$ 和 $m$ 分别为字符串 $s$ 和 $t$ 的长度。