C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で2つの数値文字列を同一にするための最小コストの求め方

問題の概要

2つの数値文字列 A と B が与えられたとき、両者を同一の文字列に揃えるために必要な最小コストを求めます。実行できる操作は「文字列から数字を1つ削除する」ことだけで、削除にかかるコストはその数字の値そのものになります。

例えば、A = "6789"、B = "7859" という2つの文字列の場合、A から「6」を、B から「5」をそれぞれ削除すれば2つの文字列が一致します。このとき必要なコストは 5 + 6 = 11 です。

解法のアプローチ:最長共通部分列(LCS)の応用

この問題は、古典的な最長共通部分列(LCS:Longest Common Subsequence)問題の変種として捉えることができます。まず文字列 A と B の LCS を求め、その結果を使って次の式から最小コストを導きます。

最小コスト = CostA + CostB − 2 × lcs_cost

  • CostA/CostB:文字列 A・B それぞれに含まれる全ての数字の合計値
  • lcs_cost:LCS に含まれる数字の合計値

LCS を構成する文字は2つの文字列に共通して残る部分であるため、削除する必要がありません。つまり、両文字列の総コストから共通部分のコストを2回分差し引けば、削除に必要な最小コストが求まるという仕組みです。

C++での実装例

#include <iostream>
using namespace std;

// メモ化再帰によって、LCSの「コスト」を計算する
int longest_common_subsequence(int dp[101][101], string a, string b, int a_len, int b_len) {
    // DPテーブルの初期化
    for (int i = 0; i < 100; i++)
        for (int j = 0; j < 100; j++)
            dp[i][j] = -1;
    // どちらかの文字列を読み終えたら終了
    if (a_len < 0 || b_len < 0) {
        return 0;
    }
    // 計算済みならキャッシュを返す
    if (dp[a_len][b_len] != -1)
        return dp[a_len][b_len];
    int res = 0;
    if (a[a_len] == b[b_len]) {
        // 文字が一致:数字の値を加算し、両方のインデックスを進める
        res = int(a[a_len] - 48) + longest_common_subsequence(dp, a, b, a_len - 1, b_len - 1);
    } else {
        // 一致しない場合:どちらか一方をスキップした結果の最大値を取る
        res = max(longest_common_subsequence(dp, a, b, a_len - 1, b_len),
                  longest_common_subsequence(dp, a, b, a_len, b_len - 1));
    }
    dp[a_len][b_len] = res;
    return res;
}

// 文字列内の全ての数字の合計(総削除コスト)を求める
int minCost(string str) {
    int cost = 0;
    for (int i = 0; i < str.length(); i++)
        cost += int(str[i] - 48);
    return cost;
}

int main() {
    string a, b;
    a = "6789";
    b = "7859";
    int dp[101][101];
    cout << "Minimum Cost to make these two numbers identical: "
         << (minCost(a) + minCost(b) - 2 * longest_common_subsequence(dp, a, b, a.length() - 1, b.length() - 1));
    return 0;
}

なお、a[a_len] - 48 という処理は、ASCII コードにおいて数字 '0' が 48 に対応していることを利用した、文字から整数値への変換です。

出力結果

Minimum Cost to make these two numbers identical: 11

計算量について

この実装ではメモ化再帰(トップダウン方式の動的計画法)を採用しています。2つの文字列の長さを N・M とすると、時間計算量および空間計算量はいずれも O(N × M) となり、文字列が長くなっても効率的に動作します。

  1. C++で2つの文字列を同一にするための最小コスト

    2つの文字列 A と B、そしてそれぞれのコスト値 CostA と CostB が与えられているとします。このとき、A と B を同一にするために必要な最小コストを求めるのが本問題です。文字列からは自由に文字を削除でき、文字列 A から1文字削除するたびに CostA、文字列 B から1文字削除するたびに CostB のコストがかかります。どの文字を削除してもコストは一定です。例として、文字列 A = wxyz、B = wyzx、CostA = 10、CostB = 20 の場合を考えてみましょう。両方の文字列から「x」を削除すると、A と B はどちらも wyz となり一致します。このときの

  2. C++で文字を削除せずに2つの文字列をアナグラムにするために必要な最小の変更回数

    問題の概要同じ長さを持つ2つの文字列が与えられたとき、文字を一切削除せずに、この2つの文字列をアナグラムにするために必要な最小の変更(置き換え)回数を求めることを考えます。アナグラムとは、使用されている文字の集合が完全に一致する2つの文字列のことです。例えば、「HELLO」と「WORLD」という2つの文字列を見てみましょう。この場合、3つの文字が互いに異なるため、必要な変更回数は3回となります。アルゴリズムの考え方この問題の解法は非常にシンプルです。以下の手順で進めます。最初の文字列に含まれる各文字の出現頻度を、サイズ26の配列に記録します。2番目の文字列を先頭から順に走査し、各文字に対応する