C++で文字列を辞書順最小の部分文字列と残りに分割する方法
問題の概要
小文字のみで構成された長さnの文字列Sが与えられます。このとき、次の条件をすべて満たす2つの空でない部分文字列PとQを見つける必要があります。
- PとQはどちらもSの部分列である
- 各インデックスiについて、S[i]はPとQのどちらか一方にのみ属する
- Pはできるだけ辞書順で最小になる
例えば、入力が S = "thelightsaber" の場合、出力は "a, thelightsber" となります。文字列中で最も辞書順に小さい文字「a」をPとし、残りの文字をすべてQとすることで、条件を満たす分割が実現できるからです。
解法のアプローチ
この問題はシンプルな貪欲法で解くことができます。Pを辞書順最小にするには、Sに含まれる最小の文字を1文字だけPとして選び、残りのすべての文字をQに割り当てればよいのです。
具体的な手順は以下の通りです。
- 文字列Sのコピーcを作成し、cを昇順にソートする
- ソート後のc[0](最小の文字)が元の文字列Sのどこに位置するかを調べ、その位置をaとする
- Sからその位置の文字を1つ削除する
- c[0](=P)と削除後のS(=Q)を出力する
C++による実装例
#include <bits/stdc++.h>
using namespace std;
void solve(string S){
string c = S;
sort(c.begin(), c.end());
int a = S.find(c[0]);
S.erase(S.begin() + a);
cout << c[0] << ", " << S << endl;
}
int main(){
string S = "thelightsaber";
solve(S);
}入力
"thelightsaber"
出力
a, thelightsber
コードの解説
このコードでは、まず文字列Sをコピーしてソートすることで、最小の文字「a」を特定しています。次に、find関数を使って元の文字列S内での「a」の位置を取得し、erase関数でその文字を削除します。最後に、最小の文字「a」と残りの文字列「thelightsber」を出力しています。
計算量については、ソートにO(n log n)、文字の検索と削除にO(n)かかるため、全体の計算量はO(n log n)となります。文字列の長さが大きくなっても効率的に動作する実装です。
-
C++で配列内に1文字だけ異なる文字列が存在するかどうかを確認する方法
文字列 s と、複数の文字列を格納した配列 A があるとします。このとき、配列の中に「元の文字列と長さが同じで、かつ1文字だけ異なる文字列」が存在するかどうかを判定する問題を考えてみましょう。 例えば、文字列が banana で、配列が [bana, orange, banaba, banapy] の場合を想定します。このうち banaba は banana と長さが同じで、1文字だけ異なるため、結果は true となります。 解決のアプローチ この問題は、以下の手順で解くことができます。 配列内の各文字列に対して、次の処理を繰り返します。 まず、その文字列の長さが s と同じかどうかを確認
-
C++で、ある文字列の部分列のうち別の文字列の部分文字列となる最長のものの長さを求める
問題の概要2つの文字列XとYが与えられたとき、Xの部分列のうち、Yの部分文字列(連続する一部分)として現れるものの中で最も長いものの長さを求めます。例えば、X = ABCD、Y = BACDBDCD の場合、出力は 3 になります。これは「ACD」がXの部分列であり、同時にYの部分文字列でもある最長の文字列だからです。動的計画法によるアプローチこの問題は動的計画法(DP)を使うことで効率的に解けます。Xの長さをn、Yの長さをmとすると、(m+1)×(n+1) のサイズのDPテーブルを作成します。DP[i][j] は「X[0…j] の部分列のうち、Y[0…i] の部分文字列となっているものの最大