1. 程式人生 > 其它 >字串A->字串B例題 :最短編輯距離、最優包含 (線性DP)

字串A->字串B例題 :最短編輯距離、最優包含 (線性DP)

AcWing 902. 最短編輯距離

給定兩個字串A和B,現在要將A經過若干操作變為B,可進行的操作有:

  • 刪除–將字串A中的某個字元刪除。
  • 插入–在字串A的某個位置插入某個字元。
  • 替換–將字串A中的某個字元替換為另一個字元。

現在請你求出,將A變為B至少需要進行多少次操作。

輸入格式
第一行包含整數n,表示字串A的長度。

第二行包含一個長度為n的字串A。

第三行包含整數m,表示字串B的長度。

第四行包含一個長度為m的字串B。

字串中均只包含大寫字母。

輸出格式
輸出一個整數,表示最少操作次數。

資料範圍
1≤n,m≤1000
輸入樣例:
10
AGTCTGACGC
11
AGTAAGTAGGC
輸出樣例:
4

題解
狀態表示 dp[i][j]

  1. 集合 : 所有把a[1~i]變成 b[1~j]的集合的操作集合
  2. 屬性 : 所有操作中操作次數最少的方案的運算元

狀態計算
狀態劃分 以對a中的第i個字母操作不同劃分

  1. 在該字母之後新增
    新增一個字母之後變得相同,說明沒有新增前a的前i個已經和b的前j-1個已經相同
    即 : dp[i][j] = dp[i][j-1] + 1

  2. 刪除該字母
    刪除該字母之後變得相同,說明沒有刪除前a中前i-1已經和b的前j個已經相同
    即 : dp[i][j] = dp[i-1][j] + 1

  3. 替換該字母

    • 替換說明對應結尾字母不同,則看倒數第二個
      即: dp[i][j] = dp[i-1][j-1] + 1
      啥也不做
    • 對應結尾字母相同,直接比較倒數第二個
      即: dp[i][j] = dp[i-1][j-1]
n = int(input())
s1 = input()
s1 = " " + s1
m = int(input())
s2 = input()
s2 = " " + s2

dp = [[1e18] * (m + 1) for i 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
    

dp[0][0] = 0

for i in range(1, n + 1):
    for j in range(1, m + 1):
        
        # 改
        if s1[i] == s2[j]:
            dp[i][j] = min(dp[i][j], dp[i - 1][j - 1])
        else: dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1)
        
        # 刪
        dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1)
        
        # 加
        dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1)
        
        
print(dp[-1][-1])
        
        

2553. 最優包含

我們稱一個字串 S 包含字串 T 是指 T 是 S 的一個子序列,即可以從字串 S 中抽出若干個字元,它們按原來的順序組合成一個新的字串與 T 完全一樣。

給定兩個字串 S 和 T,請問最少修改 S 中的多少個字元,能使 S 包含 T?

輸入格式
輸入兩行,每行一個字串。

第一行的字串為 S,第二行的字串為 T。

兩個字串均非空而且只包含大寫英文字母。

輸出格式
輸出一個整數,表示答案。

資料範圍
1≤|T|≤|S|≤1000
輸入樣例:
ABCDEABCD
XAABZ
輸出樣例:
3

題解
狀態表示 dp[i][j]

  1. 集合 : 所有把a[1~i]變成 b[1~j]的集合的操作集合
  2. 屬性 : 所有操作中操作次數最少的方案的運算元

狀態計算
狀態劃分 以對a中的第i個字母操作不同劃分

  1. 不變
    與上題不同,這是一個子序列。
    dp[i][j] = dp[i - 1][j]
  2. 替換該字母
    • 替換說明對應結尾字母不同,則看倒數第二個
      即: dp[i][j] = dp[i-1][j-1] + 1
      啥也不做
    • 對應結尾字母相同,直接比較倒數第二個
      即: dp[i][j] = dp[i-1][j-1]
s1 = input()
s2 = input()

n = len(s1)
m = len(s2)

s1 = " " + s1
s2 = " " + s2

dp = [[1e18] * (m + 1) for i in range(n + 1)]
for i in range(0, n + 1):
    dp[i][0] = 0 

for i in range(1, n + 1):
    for j in range(1, m + 1):
        
        dp[i][j] = min(dp[i][j], dp[i - 1][j])
        if s1[i] == s2[j]:
            dp[i][j] = min(dp[i][j], dp[i-1][j-1])
        else:
            dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1)
print(dp[-1][-1])