这是一道非常经典的利用栈(Stack)“后进先出”(LIFO, Last In First Out)特性来解决字符串处理的题目。
这个问题(通常对应 HDU OJ 1062 Text Reverse)的核心在于:单词内部的字符需要反转,但单词与单词之间的顺序不变。
核心思路
栈的特性是“先进后出”,这正好对应“反转”的操作。
- 遍历字符串:从左到右逐个读取字符。
- 遇到普通字符:如果是单词的一部分,将其压入栈(Push)。
- 遇到空格:说明前一个单词结束了。此时栈里保存的是该单词的所有字符(顺序是反的)。我们依次**弹出栈(Pop)**并打印字符,就能得到正序的单词。然后,直接原样输出这个空格。
- 遇到换行符/字符串结束:说明整行结束。将栈中剩余的字符全部弹出并打印(处理这一行最后一个单词),然后输出换行符。
图解流程
以输入 olleh !dlrow 为例:
- 读取 ‘o’, ‘l’, ‘l’, ‘e’, ‘h’: 依次入栈。
- 此时栈(从底到顶):
[o, l, l, e, h]
- 读取 ’ ’ (空格): 触发输出。
- 循环弹栈输出:
h->e->l->l->o(输出了 “hello”) - 输出空格本身。
- 此时屏幕显示:
hello,栈为空。
- 读取 ‘!’, ‘d’, ‘l’, ‘r’, ‘o’, ‘w’: 依次入栈。
- 此时栈(从底到顶):
[!, d, l, r, o, w]
- 读取结束: 触发输出。
- 循环弹栈输出:
w->o->r->l->d->!(输出了 “world!”) - 此时屏幕显示:
hello world!
C++ 代码实现
这里使用 C++ 标准库中的 <stack> 和 getline(比 gets 更安全且现代,但逻辑是一样的)。
cpp
#include <iostream>
#include <stack>
#include <string>
#include <cstdio> // 用于 getchar
using namespace std;
int main() {
int t;
// 读取测试用例数量
cin >> t;
// 【关键点1】吃掉输入整数T后的换行符
// 如果不写这一句,后面的 getline 会直接读到一个空行
getchar();
while (t--) {
string line;
// 读取整行包含空格的文本
getline(cin, line);
stack<char> s;
// 遍历这一行的每一个字符
for (int i = 0; i < line.length(); i++) {
if (line[i] == ' ') {
// 【关键点2】遇到空格,说明一个单词结束
// 将栈中所有字符弹出并打印
while (!s.empty()) {
cout << s.top();
s.pop();
}
// 打印空格本身
cout << " ";
} else {
// 普通字符,压入栈中
s.push(line[i]);
}
}
// 【关键点3】处理最后一个单词
// 循环结束后,栈里可能还有最后一个单词的字符(因为行尾可能没有空格)
while (!s.empty()) {
cout << s.top();
s.pop();
}
// 每个测试用例输出后换行
cout << endl;
}
return 0;
}
题目解析与注意事项
1. 为什么需要 getchar()?
当你使用 cin >> t; 输入数字(例如 3)并按下回车键时,输入缓冲区里实际是 3\n。
cin 读取了 3,但 \n(换行符)还留在缓冲区里。
如果不加 getchar(),随后的 getline(或 gets)会认为遇到了换行符,直接读取一个空字符串,导致第一个测试用例输出错误。
2. 关于 gets() 和 getline()
题目提示中提到的 gets() 是 C 语言的老函数,因为存在严重的缓冲区溢出安全隐患,在现代 C++(C++14 及以后)中已经被移除。
- 推荐做法:使用 C++ 的
getline(cin, string_variable)。 - 如果不使用 String 类:可以使用
cin.getline(char_array, size)或fgets。
3. 边界处理
最容易犯错的地方是行尾的那个单词。
循环逻辑是“遇到空格才输出栈内容”。如果一行文本的末尾没有空格(通常都是这样),循环结束后,最后一个单词还在栈里。所以必须在循环外面再写一次 while (!s.empty()) 的输出逻辑。