記事ディレクトリ
文字列内の単語を反転する
このセクションは、Code Random Notes: Code Random Notes、解説ビデオ:複雑な文字列操作をマスターする!に対応しています。| LeetCode:151. string_bilibili_bilibili 内の単語を反転します。
エクササイズ
質問リンク: 151. 文字列内の単語を反転する - LeetCode
文字列 s が与えられた場合、文字列内の単語の順序を逆にしてください。
単語はスペース以外の文字の文字列です。文字列内の単語を区切るには、s 内に少なくとも 1 つのスペースを使用します。
単語を逆の順序で単語間に 1 つのスペースで連結した結果の文字列を返します。
注: 入力文字列 s の単語間には、先頭のスペース、末尾のスペース、または複数のスペースが存在する場合があります。返される結果文字列には、単語が 1 つのスペースのみで区切られている必要があり、追加のスペースを含めることはできません。
示例 2:
输入:s = " hello world "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。
私の解決策
この問題を解決するには 2 つの困難があります。1 つは文字列内の単語を反転すること、もう 1 つは余分なスペースを削除することです。
単語を反転するには、まず文字列を反転し、次にその中の各単語を反転して、文字列内の単語の順序を反転します。具体的には、文字列をトラバースし、変数を使用して単語の最初の文字の位置を常に記録します。スペースが出現するか、文字列の次の位置に到達すると、関数を使用してlast
前の単語が反転されます。reverse
難しい点、または最適化可能な点は、冗長なスペースを削除することです。私の解決策は、while ループを使用して文字列を走査し、スペースが見つかったら、while ループを使用して、空でない文字が見つかるまで現在の位置から逆方向に検索することです。これにより、連続するスペースの文字列が決まります。erase
関数を使用してこの文字列を削除した後、単語間のスペースとして機能する別のスペースを先頭に挿入する必要があります。
class Solution {
public:
// 去除多余空格
void removeExtraSpaces(string& s) {
int cur = 0;
while (cur < s.size()) {
if (s[cur] == ' ') {
int tmp = cur;
while (s[cur] == ' ') {
cur++;
}
s.erase(tmp, cur - tmp);
// 删除空格字符串后,cur指针位置也要前移到之前第一个空格的位置
cur = tmp;
if (tmp != 0 && tmp != s.size())
s.insert(tmp, 1, ' ');
}
cur++;
}
}
string reverseWords(string s) {
removeExtraSpaces(s);
// 反转单词
int last = 0;
reverse(s.begin(), s.end());
for (int i = 0; i <= s.size(); i++) {
// 字符串最后一个字符不是空格时也要反转
if (s[i] == ' ' || i == s.size()) {
reverse(s.begin() + last, s.begin() + i);
last = i + 1;
}
}
return s;
}
};
- 時間計算量: O( n 2 n^2n2)。ここで、n は文字列 s の長さです。主にremoveExtraSpaces関数内で、消去操作と挿入操作の時間計算量はO(n)であり、ループのネスト数もO(n)レベルになる可能性があるためです。文字列反転のプロセスでは、STL ライブラリ関数 reverse が適用され、時間計算量は O(n) であるため、全体の時間計算量は O(n^2) になります。
- 空間複雑さ: O( 1 11)。データの保存に余分なスペースは使用されず、元の文字列に対する変更のみが行われるため、スペースの複雑さは一定レベルになります。
ダブルポインタ
単語を反転するダブル ポインターのソリューションは上記のソリューションと同じですが、スペースを削除するプロセスが最適化されています。上記の解決策は、2 つの while ループを使用してスペースを削除することです。ダブル ポインターの方法は、配列から要素を削除する問題に似ています。これはスペースを削除することと同じですが、スペースを削除した後、単語の間に手動でスペースを追加する必要があります。
高速ポインタを使用して文字列を走査し、低速ポインタを使用して を再割り当てします。高速ポインタがスペースと等しくないスペースを移動すると、低速ポインタが操作されて現在の単語を s の適切な位置に割り当てます。繰り返しますが、単語間のスペースは毎回手動で追加する必要があります。
このメソッドを書いたときは予想していませんでしたが、案の定、文字列や配列を操作する場合、最適化のためにダブル ポインタを使用できることがよくあります。
class Solution {
public:
// 整体思想参考https://programmercarl.com/0027.移除元素.html
// 去除所有空格并在相邻单词之间添加空格, 快慢指针。
void removeExtraSpaces(string& s) {
int slow = 0;
for (int i = 0; i < s.size(); ++i) {
if (s[i] != ' ') {
// 字符串的起始不用加空格
if (slow != 0)
s[slow++] = ' ';
// 将新的单词赋给s
while (i < s.size() && s[i] != ' ') {
s[slow++] = s[i++];
}
}
}
s.resize(slow); // slow的大小即为去除多余空格后的大小。
}
string reverseWords(string s) {
removeExtraSpaces(s);
int last = 0;
reverse(s.begin(), s.end());
for (int i = 0; i <= s.size(); i++) {
if (s[i] == ' ' || i == s.size()) {
reverse(s.begin() + last, s.begin() + i);
last = i + 1;
}
}
return s;
}
};
- 時間計算量: O( nnn )。ここで、n は文字列 s の長さです。主にremoveExtraSpaces関数では、余分なスペースの削除にダブルポインタ方式を使用し、計算量はO(n)です。単語を反転する処理では、STLライブラリ関数reverseを使用し、計算量もO( n) なので、全体の時間計算量は O(n) になります。
- 空間複雑さ: O( 1 11)。データの保存に余分なスペースは使用されず、元の文字列に対する変更のみが行われるため、スペースの複雑さは一定レベルになります。