C++で回文部分文字列を繰り返し削除して文字列を消去する最小ステップ数を求める方法
問題文
数字のみで構成された文字列が与えられます。1ステップごとに「回文になっている部分文字列」を1つ削除できるとき、この文字列をすべて削除するために必要な最小ステップ数を求めます。部分文字列を削除した後は、残りの部分同士が連結されます。
例
入力文字列が「3441213」の場合、最小2ステップで削除できます。
- まず「121」を削除します。残りの文字列は「3443」になります
- 残った「3443」は回文なので、まとめて削除します
アルゴリズム(考え方)
この問題は動的計画法(DP)を使うことで効率的に解けます。
- dp[i][j] を「部分文字列 s[i, j] を削除するのに必要なステップ数」と定義します
- 各文字は単独で削除されるか、何らかの回文部分文字列の一部として削除されます。前者の場合は、その文字だけを削除して部分問題 (i+1, j) に帰着させます
- 後者の場合は、現在の文字と同じ文字が右側に出現する位置をすべて列挙します。そのインデックスを K とすると、問題は2つの部分問題 (i+1, K−1) と (K+1, j) に分解できます
- (i+1, K−1) という部分問題に到達できるのは、両端の同じ文字を先に削除すれば中間の部分文字列だけが残るからです
- さらに、先頭の2文字が同じ場合には、直接部分問題 (i+2, j) に帰着できるケースも考慮する必要があります
C++での実装例
#include <bits/stdc++.h>
using namespace std;
int getMinRequiredSteps(string str) {
int n = str.length();
int dp[n + 1][n + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
dp[i][j] = 0;
}
}
for (int len = 1; len <= n; len++) {
for (int i = 0, j = len - 1; j < n; i++, j++) {
if (len == 1)
dp[i][j] = 1;
else {
dp[i][j] = 1 + dp[i + 1][j];
if (str[i] == str[i + 1]) {
dp[i][j] = min(1 + dp[i+ 2][j], dp[i][j]);
}
for (int K = i + 2; K <= j; K++){
if (str[i] == str[K]) {
dp[i][j] =
min(dp[i+1][K-1] + dp[K+1][j], dp[i][j]);
}
}
}
}
}
return dp[0][n - 1];
}
int main() {
string str = "3441213";
cout << "Minimum required steps: " <<
getMinRequiredSteps(str) << endl;
return 0;
}
上記のプログラムをコンパイルして実行すると、次の出力が得られます。
実行結果
Minimum required steps: 2
計算量について
この解法は区間DPに基づいているため、長さ n の文字列に対して時間計算量は O(n³)、空間計算量は O(n²) となります。短〜中程度の長さの文字列であれば十分に高速に動作します。
-
C++で文字列内の最初に繰り返される単語を検索する方法
この問題では、スペースで区切られた複数の単語からなる文字列 str が与えられます。私たちのタスクは、文字列の中で最初に繰り返し出現する単語を見つけることです。つまり、「2つのスペースに挟まれた単語」の中から、文字列内で重複して現れる最初のものを特定する必要があります。問題を理解するための例入力 : str = C program are easy to program 出力 : program解決アプローチこの問題に対するシンプルな解決策は、ハッシュマップ(unordered_map)というデータ構造を利用することです。まず、文字列を単語ごとに分割しながら読み込み、各単語とその出現回数をハッ
-
C++で文字列の部分文字列の総数を求める方法を解説
この記事では、与えられた文字列から作成できる空でない部分文字列の個数を求める方法について解説します。入力 : string = "moon" 出力 : 10 説明 : 部分文字列は m、o、o、n、mo、oo、on、moo、oon、moon の 10 個です。 入力 : string = "yellow" 出力 : 21解法のアプローチ文字列の長さを n とします。上の例からも分かるように、考えられるすべての部分文字列の個数を求めるには、長さ n、(n-1)、(n-2)、(n-3)、……2、1 の部分文字列の個数を順に加算していく必要があります。部分文