C++で文中の単語を長さ順に並べ替える方法
本記事では、C++を用いて文中の単語を長さの昇順に並べ替えるアルゴリズムを解説します。
問題の概要
ある文字列が与えられ、この文字列は「文(センテンス)」と呼ばれるものとします。文は次の形式を満たしています。
- 先頭の文字は必ず大文字である。
- 各単語は1つの半角スペースで区切られている。
求められるのは、文中のすべての単語を長さが短い順(昇順)に並べ替えた新しい文を作ることです。ここで重要なルールとして、同じ長さの単語がある場合は、元の文中での出現順序を維持する必要があります。これはいわゆる「安定なソート(stable sort)」の考え方です。
最終的に、これらのルールを適用した結果の文字列を返します。
入出力例
たとえば、入力が "I love to code in cpp" の場合、各単語の長さは次のようになります。
- I(1文字)、to(2文字)、in(2文字)、cpp(3文字)、love(4文字)、code(4文字)
したがって、出力は "I to in cpp love code" となります。「to」と「in」、「love」と「code」はそれぞれ同じ長さのため、元の順序が保たれている点に注目してください。
解法のアプローチ
この問題は、次の手順で解くことができます。
- 文の先頭文字を小文字に変換する(ソート後に元に戻すため)。
- 文をスペースで分割し、単語の配列
xを作成する。 (単語, 元のインデックス)のペアを格納する配列sを用意する。- 各単語とそのインデックスをペアとして
sに追加していく。 sを「単語の長さ」を基準にソートする。長さが同じ場合は「元のインデックス」で比較することで、安定性を保証する。- 結果格納用の空文字列
retを用意し、ソート済みの単語を順番に連結する。単語間にはスペースを挿入する。 - 最後に
retの先頭文字を大文字に戻して返す。
C++による実装例
それでは、上記の手順を実際のコードで確認してみましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
vector <string> split(string& s, char delimiter){
vector <string> tokens;
string token;
istringstream tokenStream(s);
while(getline(tokenStream, token, delimiter)){
tokens.push_back(token);
}
return tokens;
}
static bool cmp(pair <string, int>& a, pair <string, int>& b){
if(a.first.size() != b.first.size()) return a.first.size() < b.first.size();
return a.second < b.second;
}
string arrangeWords(string text) {
text[0] += 'a' - 'A';
vector<string> x = split(text, ' ');
vector<pair<string, int> > s;
for (int i = 0; i < x.size(); i++)
s.push_back({ x[i], i });
sort(s.begin(), s.end(), cmp);
string ret = "";
for (int i = 0; i < s.size(); i++) {
ret += s[i].first;
if (i != s.size() - 1)
ret += ' ';
}
ret[0] += 'A' - 'a';
return ret;
}
};
main(){
Solution ob;
cout << (ob.arrangeWords("I love to code in cpp"));
}
コードのポイント
- split関数:
istringstreamとgetlineを組み合わせることで、指定した区切り文字(ここではスペース)で文字列を簡単に分割できます。 - 比較関数cmp: 単語の長さが異なる場合は長さで比較し、同じ場合はペアに保持しておいた元のインデックスで比較します。これにより、同じ長さの単語の相対的な順序が崩れません。
- 大文字・小文字の処理: ソートの前に先頭文字を小文字化し、完成後に再び大文字へ戻すことで、出力フォーマットの要件を満たしています。
実行結果
入力
"I love to code in cpp"
出力
I to in cpp love code
計算量について
単語数を n、各単語の平均的な長さを考慮すると、ソート部分が支配的となり、時間計算量は O(n log n) となります。また、単語とインデックスを保持するため、空間計算量は O(n) です。文字列の分割と再構築も線形時間で処理できるため、全体として効率的なアルゴリズムと言えます。
まとめ
この問題の鍵となるのは、「同じ長さの単語は元の順序を維持する」という安定性の要件です。(単語, インデックス) のペアを作り、カスタム比較関数でソートすることで、シンプルかつ確実に要件を満たす実装が可能になります。C++では stable_sort を使うという選択肢もありますが、インデックスを明示的に扱うこの手法は、他の言語にも応用しやすい汎用的なパターンです。
-
Pythonで文字列内の単語数をカウントする3つの方法
はじめに Pythonでは、テキスト処理の一環として「文章中に単語がいくつ含まれているか」を数える場面がよくあります。本記事では、与えられた文字列から単語数をカウントするための代表的な3つのアプローチを、具体的なコード例と実行結果とともにわかりやすく解説します。 問題の定義 問題文: 与えられた文字列の中に含まれる単語の数を数える。例えば、「Tutorials point is a learning platform」という文字列には、6個の単語が含まれています。 方法1:split()関数を使う split()関数は、文字列をスペースを区切り文字として分割し、リストを返します。引数を指定
-
Pythonで文中の単語数をカウントする方法|split()とisalpha()を使った2つのアプローチ
本記事では、Pythonを使って文章中の単語数を数えるための解法と、その具体的なアプローチについて詳しく解説します。 問題定義 ある文が与えられたとき、その文に含まれる単語の総数をカウントするプログラムを作成します。 ここでは、以下の2つのアプローチを取り上げます。 アプローチ1: split()関数を使用する方法 アプローチ2: strip()関数とisalpha()関数を組み合わせる方法 アプローチ1:split()関数を使う方法 最もシンプルな方法は、文字列に対してsplit()関数を適用するやり方です。split()はデフォルトで空白文字(スペースやタブなど)を区切りとして文字列を