C++で解く整然キュー(Orderly Queue)問題:辞書順最小の文字列を求めるアルゴリズム
問題の概要
小文字のみで構成される文字列 S が与えられます。この文字列に対して、任意の回数だけ次の操作を行うことができます。
操作: 先頭の K 文字の中から1文字を選び、それを取り除いて文字列の末尾に移動させる。
このとき、操作を何度行ってもよいとして、最終的に得られる文字列のうち辞書順で最小のものを求めてください。
例えば、入力が "cabaa"、K = 3 の場合、答えは "aaabc" になります。
解き方のアプローチ
ケース1:K > 1 の場合
Kが2以上のときは、実質的にどのような並べ替えも可能になります。これは、隣接する2文字の入れ替え(バブルソートと同じ要領)を繰り返すことで任意の順列が構成できるためです。したがって、以下の手順で答えが得られます。
文字列 S をソートする
ソート結果をそのまま返す
ケース2:K = 1 の場合
Kが1のときは、常に先頭の1文字しか末尾に移動できません。つまり、操作によって文字列を回転させることしかできないため、考えられるすべての回転を試し、その中で最も小さい文字列を採用します。
アルゴリズムの手順
ret := S(初期値として元の文字列を設定)
n := 文字列 S の長さ
i := 1 から n 未満の間、i を1ずつ増やしながら以下を繰り返す:
S := S の先頭の1文字を切り取り、末尾に追加する(1回転)
もし S < ret であれば、ret = S と更新する
最後に ret を返す
それでは、以下の実装例を見て理解を深めましょう。
C++による実装例
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
string orderlyQueue(string S, int K) {
if(K > 1){
sort(S.begin(), S.end());
return S;
}
string ret = S;
int n = S.size();
for(int i = 1; i < n; i++){
S = S.substr(1) + S.substr(0, 1);
if(S < ret) ret = S;
}
return ret;
}
};
main(){
Solution ob;
cout << (ob.orderlyQueue("cabaa", 3));
}
入力
"cabaa", 3
出力
aaabc
計算量について
K = 1 の場合: 回転を最大 n 回行い、毎回の文字列比較に O(n) かかるため、全体の時間計算量は O(n²) となります。
K > 1 の場合: 処理はソートが支配的となるため、時間計算量は O(n log n) です。
-
C/C++で学ぶ優先度付きキュー(プライオリティキュー)の基本と実装
優先度付きキュー(プライオリティキュー)とは、要素に割り当てられた「優先度」に従って挿入・削除が行われる特殊なキューの一種です。優先度は0〜10の整数値で表現され、0が最も高い優先度、10が最も低い優先度を意味します。病院の救急外来で重症患者が待ち順序に関係なく先に診察されるように、重要度の高いタスクを優先的に処理したい場面で活躍するデータ構造です。 優先度付きキューを守る2つの基本ルール 優先度付きキューを実装する際には、次の2つのルールに従います。 優先度の高い要素ほど先に処理される — 最も優先度の高いデータは、優先度の低いデータよりも先に実行されます。 同じ優先度なら追加順に処理さ
-
【C++】キューを使って二分探索木(BST)のパスを反転する方法
問題の概要 二分探索木(BST)が与えられ、特定のキーからルートに至るパス上のノードの値を反転することが求められます。 たとえば次のようなイメージです。 解決のためのアプローチ このアプローチでは、まず空のキューを用意してルートから探索を開始します。木を辿りながら経路上のノードの値を順番にキューへプッシュしていき、目的のキーを持つノードが見つかったら、再帰の帰り道でキューの先頭から順に値を書き戻します。こうすることで、パス上のノードの値がきれいに反転されます。 C++での実装例 #include <bits/stdc++.h> using namespace std; stru