Opening the reading…
Opening the reading…
2 POINTERS › TWO POINTER ON STRINGS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →Reversing the complete string puts the words in reverse order, but also reverses every word internally. For example, the characters in the first word become the characters of the last word, but in backward order. Reversing each word after the whole-string reversal restores its letters while keeping its new position, so the two reversals cancel at the character level and remain effective at the word level.
The spaces need separate treatment because the result must contain exactly one space between words. While scanning from left to right, skip every space before a word, copy the word to the next compact position, reverse that copied range, and append one separator. The read pointer explores the original content, while the write pointer builds the cleaned result inside the same string.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The whole string is reversed once, each character is read at most once during compaction, and each copied word is reversed once. The word ranges are disjoint, so the total number of character operations across all local reversals is at most n. |
| Space | O(1) extra | The output remains inside the mutable input string, so output storage is excluded. The algorithm uses only indices and the in-place reverse operation; its extra working space stays constant, including when the input has the worst shape of many short words or many spaces. |
#include <algorithm>
#include <string>
using namespace std;
class Solution {
public:
string reverseWords(string s) {
reverse(s.begin(), s.end());
int n = static_cast<int>(s.size());
int i = 0;
int left = 0;
int right = 0;
while (i < n) {
while (i < n && s[i] == ' ') {
++i;
}
if (i == n) {
break;
}
while (i < n && s[i] != ' ') {
s[right++] = s[i++];
}
reverse(s.begin() + left, s.begin() + right);
s[right++] = ' ';
left = right;
}
s.resize(right - 1);
return s;
}
};The key invariant is that the prefix before right already contains the answer's recovered words, separated by single spaces, while i has consumed the corresponding portion of the reversed input. The assignment s[right++] = s[i++] is safe because right never moves ahead of i during compaction. Once a word is copied, left and right isolate exactly that word, so its local reverse cannot disturb earlier output.
A competent alternative scans from the end, skips spaces, identifies each word, and appends it directly to a new result string. It avoids the temporary reversed characters and is often easier to explain, but it uses O(n) extra space for the result and does not satisfy the in-place goal suggested by the two-pointer method. It is a different arrangement, not an optimisation.
#include <string>
using namespace std;
class Solution {
public:
string reverseWords(string s) {
string result;
int i = static_cast<int>(s.size()) - 1;
while (i >= 0) {
while (i >= 0 && s[i] == ' ') {
--i;
}
if (i < 0) {
break;
}
int end = i;
while (i >= 0 && s[i] != ' ') {
--i;
}
if (!result.empty()) {
result.push_back(' ');
}
result.append(s, i + 1, end - i);
}
return result;
}
};