C++で隣接する重複文字を含まない最も近い文字列を出力する方法
問題概要
この問題では、1つの文字列が与えられます。私たちのタスクは、元の文字列に最も近く、かつ隣接する重複文字を一切含まない文字列を出力することです。
具体例を見てみましょう。
入力:string = "good" 出力:goad
この例では、インデックス1と2の文字が同じであるため、インデックス2の文字を変更しています。こうすることで「good」から最も近い形を保ちながら、隣接する重複を取り除いています。
解決アプローチ
この問題を解くには、文字列を走査しながら、隣接する2つの文字が同じかどうかを確認します。同じ場合は、後ろ側の文字(i番目とi+1番目が一致しているならi+1番目)を変更します。この解法では貪欲アルゴリズム(グリーディ法)を使用し、隣接して一致している各ペアに対して1回の変更を行います。
注意すべき点は、文字を変更する際に前後の要素をすべて確認することです。つまり、i番目の文字を変更する場合、変更後にi番目とi+1番目の文字が異なる状態になるようにしなければなりません。これを怠ると、修正の過程で新たな重複を作り出してしまう可能性があります。
アルゴリズムの手順
- 文字列を先頭から順に走査します。
- 現在の文字が直前の文字と一致した場合、その文字を一旦「a」に置き換えます。
- 置き換えた文字が、前の文字とも次の文字とも一致しなくなるまで、1ずつ増やしていきます。
- これを文字列の末尾まで繰り返し、最終的な結果を出力します。
実装例
上記の解法を実装したプログラムは以下の通りです。
#include <iostream>
#include <string.h>
using namespace std;
void printStringWithNoDuplicates(string str){
int len = str.length();
for (int i = 1; i < len; i++){
if (str[i] == str[i - 1]){
str[i] = 'a';
while (str[i] == str[i - 1] || (i + 1 < len && str[i] == str[i + 1])) str[i]++;
i++;
}
}
cout<<str;
}
int main(){
string str = "good";
cout<<"元の文字列 : "<<str<<endl;
cout<<"隣接する重複文字を含まない文字列 : ";
printStringWithNoDuplicates(str);
return 0;
}出力結果
元の文字列 : good 隣接する重複文字を含まない文字列 : goad
まとめ
このように、貪欲法を用いて文字列を一度だけ走査することで、時間計算量O(n)で隣接する重複文字を含まない最も近い文字列を効率的に求めることができます。各位置での変更は必要最小限にとどめられるため、元の文字列からの変化も最小に抑えられます。英小文字のみで構成された文字列であれば、必ず26文字の中から前後と異なる文字を選べるため、この手法は常に有効です。
-
なぜC++には仮想コンストラクターが存在しないのか?
仮想機構が機能するための前提条件 C++における仮想(virtual)機構は、「基底クラスのポインタが派生クラスのオブジェクトを指している」という状況でのみ動作します。この仕組みを理解することが、仮想コンストラクターに関する議論を理解する第一歩となります。 コンストラクターをvirtualにできない理由 C++では、コンストラクターを仮想関数にすることはできません。その理由は、クラスのコンストラクターが実行される時点では、メモリ上に仮想関数テーブル(vtable)がまだ作成されておらず、仮想関数へのポインタ(vptr)も定義されていないためです。 仮想関数の仕組みはvtableとvptrに依
-
C++のvectorとは?仕組みと主要なメンバ関数の使い方を実行例つきで解説
C++のvector(ベクトル)とは C++のstd::vectorは、いわば「賢い配列」です。要素の挿入や削除が行われるたびに、動的配列として自動的にサイズを伸縮でき、内部の記憶域(メモリ)の管理までもコンテナ側が引き受けてくれます。そのため、開発者はメモリの確保や解放を意識することなく、C++で最も広く使われるコンテナとして活用できます。 vectorの要素は連続したメモリ領域に配置されます。この特徴により、インデックスを使ったランダムアクセスが高速であるだけでなく、イテレータを使った順次アクセス(走査)にも対応しています。さらに、データの挿入・削除は、先頭・中間・末尾のいずれの位置に対し