Remove Vowels
英字で構成された文字列 s が与えられます。すべての母音を削除して得られる文字列を返してください。母音は小文字または大文字の a、e、i、o、u です。この場合、y は母音ではありません。残った文字は、順序も大文字・小文字もそのままにします。
関数
- sstring
- クリーニングする英字の文字列
- 戻り値string
- sからすべての母音を取り除き、その他の文字は元の順序のままにしたもの
制約
1 ≤ s.length ≤ 3 × 104sには英字(aからz、AからZ)のみが含まれます。sには母音ではない文字が少なくとも1つ含まれているため、答えが空になることはありません。
例
- 入力
- s = "Interview"
- 出力
- "ntrvw"
- 説明
InterviewからI、e、i、eを削除すると、n、t、r、v、wがその順番で残ります。大文字のIも母音なので、削除します。
- 入力
- s = "rhythm"
- 出力
- "rhythm"
- 説明
rhythmにはa、e、i、o、uが含まれていないため、何も削除されません。yは母音のリストに含まれていないので、そのまま残ります。
- 入力
- s = "EuropeanUnion"
- 出力
- "rpnnn"
- 説明
EuropeanUnionの13文字のうち8文字は、先頭が大文字のEとUを含め、母音です。残る5つの子音r、p、n、n、nは順序を保ち、rpnnnと読めます。
提出時に隠しテスト+17件
発展問題
テキストにÉやöのようなUnicode文字を含められるとしたらどうでしょうか?そのうちどれが母音で、判定方法はどのように変わるでしょうか?
ヒント
1つずつ開いてください。開くたびに少しずつ答えに近づきます。
sのどの文字が答えに含まれますか。また、それらの順序は変わりますか?母音を削除するのではなく、残す文字から新しい文字列を作りましょう。
A、E、I、O、Uも母音であることを忘れないでください。文字列を1回走査します。
aeiouAEIOUのいずれでもない文字をすべてビルダーまたはリストに追加し、最後に結合して文字列にします。
解説
文字列の途中から文字を削除するのは、一度に1文字ずつ削除するとコストがかかります。空いた箇所より後ろのすべての文字がずれてしまうためです。より良い方法は、答えを組み立てることです。文字列を一度走査し、母音ではない文字をすべてコピーします。大文字の母音と、結果をどのように組み立てるかが重要なポイントです。
各母音をそれぞれ別のパスで削除する
考え方
ほとんどの言語では、文字列からある1文字のコピーをすべて削除するのに、1回の呼び出しで済みます。その文字を何もないものに置き換えます。これを a e i o u A E I O U のそれぞれについて1回ずつ、合計10回行えば、母音は残りません。子音には一切触れないため、順序も大文字・小文字もそのままです。
Interview の場合、e の処理で Intrviw になり、i の処理で Intrvw になり、I の処理で ntrvw になります。残りの7回の処理では、削除するものは見つかりません。
各処理では現在の文字列全体を読み取るため、処理量は約 10n 文字分の手順です。10は定数なので、これは依然として O(n) ですが、文字が 3 × 10^4 個の場合、1回の走査で必要な 3 × 10^4 手順に対して、3 × 10^5 手順を要します。
アルゴリズム
- 10個の母音字
aeiouAEIOUを1文字ずつ取り出します。 - それぞれについて、
s内にあるその文字をすべて何もない状態に置き換えます。 - 10回の処理後、
sに残ったものを返します。
def removeVowels(s):
for vowel in "aeiouAEIOU":
s = s.replace(vowel, "") # one full pass for this letter
return s子音を保持する1回のパス
考え方
作業を逆にします。母音を削除するのではなく、それ以外をすべて集めます。sを一度走査し、各文字が10個の母音字のいずれかかどうかを確認します。そうでなければ、結果に追加します。読み取り順に追加し、文字を一切変更しないため、子音の順序と大文字・小文字は入力されたとおりになります。
EuropeanUnionの場合、走査ではE、u、o、e、a、U、i、oを飛ばし、r、p、n、n、nを追加します。結果はrpnnnです。
各文字の処理には、定数時間の判定が1回かかります(集合の検索、switch、または10文字の文字列内の検索)。したがって、時間計算量はO(n)です。文字をビルダーまたはリストに集め、最後に一度だけ文字列に変換します。不変文字列を+=で伸ばすと、毎回コピーが発生します。出力そのものにO(n)の空間を使います。
アルゴリズム
- 結果を格納する空のビルダーを作成します。
sを1文字ずつ調べます。- その文字が
aeiouAEIOUのいずれでもない場合は、ビルダーに追加します。 - ビルダーを文字列として返します。
def removeVowels(s):
vowels = set("aeiouAEIOU")
kept = []
for ch in s:
if ch not in vowels:
kept.append(ch)
# Joining a list once avoids rebuilding the string on every character.
return "".join(kept)
落とし穴と境界ケース
誤答の多くは、母音の判定、または結果の文字列の作り方が原因です。
- 大文字の母音を見落とす。
aeiouだけを調べると、InterviewはntrvwではなくIntrvwになります。10個すべての文字を調べるか、判定の前に文字を小文字に変換し、出力には元の文字を使います。 - 残す文字の大文字・小文字を変えてしまう。文字列全体を小文字にして判定を簡単にすると、
QUEUEINGはQNGではなくqngになります。判定に使うコピーだけを小文字に変換し、元の文字を追加します。 - インデックスを前に進めながら削除する。
s[i]を削除すると、次の文字が位置iに移動し、その後i++でその文字を飛ばしてしまうため、aabはabになります。新しい文字列を作るか、読み取り位置と書き込み位置を別々にして処理します。 - ループ内で
+=を使って変更できない文字列を伸ばす。JavaやC#では、各ステップで文字列全体がコピーされるため、3 × 10^4文字の場合、約4.5 × 10^8回の文字コピーが発生します。ビルダーまたはリストを使い、最後に一度だけ結合します。
よくある質問4
文字列から母音を削除するにはどうすればよいですか?
文字列を一度走査し、大文字・小文字を問わず a、e、i、o、u 以外の各文字をビルダーまたはリストにコピーします。最後にそれらを結合して文字列にします。残す文字の順序と大文字・小文字はそのままです。
母音を削除する時間計算量はどれくらいですか?
1回の走査にかかる時間は O(n) です。各文字に対して定数時間の母音判定を1回行うためです。最悪の場合、つまり s に母音がまったく含まれない場合、出力に必要な空間は O(n) です。母音ごとに replace を1回呼び出す方法も O(n) ですが、文字列を10回読み取ります。
正規表現で母音を削除できますか?
はい。ほとんどの言語では、パターン [aeiouAEIOU] を空文字列に置き換えることで、1回の呼び出しで実現できます。実行時間はループと同じ O(n) ですが、面接官は通常、母音の判定方法や結果の組み立て方を確認するために、ループを書くよう求めます。
文字列から母音をその場で削除しないのはなぜですか?
中央から1文字削除すると、その後のすべての文字が左にずれるため、削除を何度も行うと O(n²) のコストがかかることがあります。2つのインデックスを使えば、O(n) でその場で処理できます。一方のインデックスですべての文字を読み取り、もう一方のインデックスで残す文字を順に書き込みます。ただし、ほとんどの言語では文字列を変更できないため、新しい文字列を作るのが自然な方法です。
似た問題
同じ考え方を使う問題です。2〜3問解くとパターンが身につきます。
Python
def removeVowels(s):
# ここにコードを書いてくださいケース1
ケース2
ケース3
入力
s = "Interview"
期待値
"ntrvw"