Files
Yudong Jin 7257641e0e Retranslate the complete Japanese online edition from Chinese (#2008)
* Retranslate the complete Japanese online edition from Chinese

* Fit Japanese homepage labels and localize heading permalinks

* Point Japanese contribution instructions to the Japanese preview

* Replace locale conditionals with multilingual UI tables
2026-10-30 02:12:26 +08:00

148 lines
5.0 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* File: edit_distance.swift
* Created Time: 2023-07-16
* Author: nuomi1 (nuomi1@qq.com)
*/
/* 編集距離:全探索 */
func editDistanceDFS(s: String, t: String, i: Int, j: Int) -> Int {
// s と t がともに空の場合、0 を返す
if i == 0, j == 0 {
return 0
}
// s が空の場合、t の長さを返す
if i == 0 {
return j
}
// t が空の場合、s の長さを返す
if j == 0 {
return i
}
// 2 つの文字が等しい場合、その 2 文字をスキップする
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
}
// 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 1
let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1)
let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j)
let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
// 最小編集回数を返す
return min(min(insert, delete), replace) + 1
}
/* 編集距離:メモ化探索 */
func editDistanceDFSMem(s: String, t: String, mem: inout [[Int]], i: Int, j: Int) -> Int {
// s と t がともに空の場合、0 を返す
if i == 0, j == 0 {
return 0
}
// s が空の場合、t の長さを返す
if i == 0 {
return j
}
// t が空の場合、s の長さを返す
if j == 0 {
return i
}
// 記録済みなら、その値をそのまま返す
if mem[i][j] != -1 {
return mem[i][j]
}
// 2 つの文字が等しい場合、その 2 文字をスキップする
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
}
// 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 1
let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1)
let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j)
let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
// 最小編集回数を記録して返す
mem[i][j] = min(min(insert, delete), replace) + 1
return mem[i][j]
}
/* 編集距離:動的計画法 */
func editDistanceDP(s: String, t: String) -> Int {
let n = s.utf8CString.count
let m = t.utf8CString.count
var dp = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1)
// 状態遷移:先頭行と先頭列
for i in 1 ... n {
dp[i][0] = i
}
for j in 1 ... m {
dp[0][j] = j
}
// 状態遷移:残りの行と列
for i in 1 ... n {
for j in 1 ... m {
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
// 2 つの文字が等しい場合、その 2 文字をスキップする
dp[i][j] = dp[i - 1][j - 1]
} else {
// 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 1
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1
}
}
}
return dp[n][m]
}
/* 編集距離:空間を最適化した動的計画法 */
func editDistanceDPComp(s: String, t: String) -> Int {
let n = s.utf8CString.count
let m = t.utf8CString.count
var dp = Array(repeating: 0, count: m + 1)
// 状態遷移:先頭行
for j in 1 ... m {
dp[j] = j
}
// 状態遷移:残りの行
for i in 1 ... n {
// 状態遷移:先頭列
var leftup = dp[0] // dp[i-1, j-1] を一時保存する
dp[0] = i
// 状態遷移:残りの列
for j in 1 ... m {
let temp = dp[j]
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
// 2 つの文字が等しい場合、その 2 文字をスキップする
dp[j] = leftup
} else {
// 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 1
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1
}
leftup = temp // 次の反復の dp[i-1, j-1] に更新する
}
}
return dp[m]
}
@main
enum EditDistance {
/* 実行用コード */
static func main() {
let s = "bag"
let t = "pack"
let n = s.utf8CString.count
let m = t.utf8CString.count
// 全探索
var res = editDistanceDFS(s: s, t: t, i: n, j: m)
print("\(s) を \(t) に変更するには最低 \(res) 回の編集が必要")
// メモ化探索
var mem = Array(repeating: Array(repeating: -1, count: m + 1), count: n + 1)
res = editDistanceDFSMem(s: s, t: t, mem: &mem, i: n, j: m)
print("\(s) を \(t) に変更するには最低 \(res) 回の編集が必要")
// 動的計画法
res = editDistanceDP(s: s, t: t)
print("\(s) を \(t) に変更するには最低 \(res) 回の編集が必要")
// 空間を最適化した動的計画法
res = editDistanceDPComp(s: s, t: t)
print("\(s) を \(t) に変更するには最低 \(res) 回の編集が必要")
}
}