1. 算法题目解析:敲笨钟问题
这道算法题的核心逻辑是处理输入的多行诗句,判断每行诗句是否符合特定格式要求,并根据判断结果进行相应的输出处理。题目要求我们检查诗句中是否存在特定的"ong"模式,并根据检查结果决定输出原诗句还是替换为固定内容。
1.1 题目要求详解
题目具体要求可以分解为以下几个关键点:
- 输入n个独立的诗句,每个诗句以句号结尾
- 对于每个诗句,需要检查两个位置:
- 诗句中逗号前三个字符是否为"ong"
- 诗句末尾句号前三个字符是否为"ong"
- 如果两个位置都满足"ong"条件,则将诗句最后三个词替换为"qiao ben zhong."
- 如果任一位置不满足条件,则输出"Skipped"
1.2 输入输出格式分析
输入格式需要注意:
- 第一行是一个整数n,表示后续有n个诗句需要处理
- 每个诗句由多个单词组成,以空格分隔,以句号结尾
- 每个单词可能包含标点符号(主要是逗号和句号)
输出格式要求:
- 对于符合条件的诗句,输出替换最后三个词后的完整诗句
- 对于不符合条件的诗句,直接输出"Skipped"
- 每个输出占一行
2. 解题思路与算法设计
2.1 整体处理流程
解题的整体思路可以分为以下几个步骤:
- 读取输入的n值
- 循环处理n个诗句:
a. 读取并存储当前诗句的所有单词
b. 定位诗句中的逗号位置
c. 检查逗号前和句号前是否符合"ong"条件
d. 根据检查结果决定输出内容
2.2 关键算法实现细节
2.2.1 诗句读取与存储
使用vector
- 诗句长度不固定,需要动态数据结构
- 需要频繁按索引访问单词,vector的随机访问效率高
- 需要知道诗句的总单词数,vector的size()方法可直接获取
代码实现中使用了双重循环读取:
- 外层循环处理n个诗句
- 内层循环读取当前诗句的各个单词,直到遇到包含句号的单词
2.2.2 逗号位置定位
定位逗号位置的算法要点:
- 遍历vector中的每个单词
- 对每个单词遍历每个字符,查找逗号
- 记录第一个逗号所在的单词索引
- 同时记录该单词中逗号的位置(虽然题目中似乎未使用这个信息)
2.2.3 "ong"模式检查
检查逻辑需要注意:
- 需要确保字符串长度足够(至少4个字符,因为要检查倒数第2-4个字符)
- 检查逗号所在单词的倒数第2-4个字符是否为'o','n','g'
- 检查最后一个单词的倒数第2-4个字符(句号前)是否为'o','n','g'
- 注意字符索引的计算方式,避免越界访问
3. 代码实现详解
3.1 主程序结构
cpp复制#include<bits/stdc++.h>
using namespace std;
int main() {
int n; cin >> n;
while(n--) {
// 诗句处理逻辑
}
return 0;
}
主程序结构简洁明了:
- 包含常用头文件<bits/stdc++.h>
- 读取诗句数量n
- 循环处理每个诗句
3.2 诗句读取实现
cpp复制vector<string> s;
int cnt = 0;
while(1) {
string s1;
cin >> s1;
cnt++;
if(s1.find('.') == string::npos)
s.push_back(s1);
else {
s.push_back(s1);
break;
}
}
这段代码实现了:
- 动态读取单词直到遇到包含句号的单词
- 使用vector存储所有单词
- cnt变量似乎未被使用,可以优化移除
3.3 逗号位置查找
cpp复制int ll = 0, falg = 0;
for(int i = 0; i < s.size(); i++) {
for(char c : s[i]) {
if(c == ',') {
ll = i;
falg = 1;
break;
}
}
if(falg == 1) break;
}
这段代码实现了:
- 遍历所有单词查找第一个逗号
- 记录包含逗号的单词索引(ll)
- 使用标志位(falg)优化查找过程
3.4 条件检查与输出处理
cpp复制int l = s.size();
int l1 = s[l-1].size();
int l2 = s[ll].size();
if(l1 >= 4 && l2 >= 4 &&
s[l-1][l1-2] == 'g' && s[l-1][l1-3] == 'n' && s[l-1][l1-4] == 'o' &&
s[ll][l2-2] == 'g' && s[ll][l2-3] == 'n' && s[ll][l2-4] == 'o') {
s[l-1] = "zhong.";
s[l-2] = "ben";
s[l-3] = "qiao";
for(int i = 0; i < l; i++) {
if(i > 0) cout << " ";
cout << s[i];
}
} else {
cout << "Skipped";
}
cout << endl;
这段核心逻辑实现了:
- 获取必要的位置信息
- 诗句总单词数(l)
- 最后一个单词的长度(l1)
- 包含逗号的单词长度(l2)
- 检查"ong"条件
- 确保足够长度避免越界
- 检查句号前三个字符
- 检查逗号前三个字符
- 条件满足时替换最后三个单词
- 输出处理结果
4. 算法优化与改进建议
4.1 当前实现的优缺点分析
优点:
- 逻辑清晰,直接对应题目要求
- 使用vector存储方便单词管理
- 条件检查严谨,避免数组越界
- 输出格式处理得当
缺点和改进空间:
- cnt变量未被使用,可以移除
- falg变量拼写错误,应改为flag
- 可以提取条件检查为独立函数提高可读性
- 最后一个单词的句号处理可以更优雅
- 可以考虑使用string的substr方法简化字符检查
4.2 优化后的代码实现
cpp复制#include <iostream>
#include <vector>
using namespace std;
bool checkOng(const string& str, int pos) {
return pos >= 3 &&
str[pos-1] == 'g' &&
str[pos-2] == 'n' &&
str[pos-3] == 'o';
}
int main() {
int n; cin >> n;
cin.ignore(); // 清除输入缓冲区
while(n--) {
vector<string> words;
string word;
int commaPos = -1;
// 读取诗句
while(cin >> word) {
words.push_back(word);
if(word.find('.') != string::npos) break;
}
// 查找逗号位置
for(int i = 0; i < words.size(); i++) {
if(words[i].find(',') != string::npos) {
commaPos = i;
break;
}
}
// 检查条件
bool valid = false;
if(commaPos != -1) {
string& lastWord = words.back();
string& commaWord = words[commaPos];
size_t dotPos = lastWord.find('.');
size_t commaInWord = commaWord.find(',');
if(dotPos != string::npos && commaInWord != string::npos) {
valid = checkOng(lastWord, dotPos) &&
checkOng(commaWord, commaInWord);
}
}
// 处理输出
if(valid) {
int last = words.size() - 1;
words[last] = "zhong.";
words[last-1] = "ben";
words[last-2] = "qiao";
for(int i = 0; i < words.size(); i++) {
if(i > 0) cout << " ";
cout << words[i];
}
} else {
cout << "Skipped";
}
cout << endl;
}
return 0;
}
优化点说明:
- 提取条件检查为独立函数checkOng
- 使用find方法定位标点位置
- 更清晰的变量命名
- 添加输入缓冲区处理
- 更严谨的条件检查逻辑
5. 常见问题与调试技巧
5.1 典型错误与解决方法
-
数组越界问题
- 现象:运行时出现段错误
- 原因:未检查字符串长度就直接访问特定位置
- 解决:确保在访问s[l-1][l1-2]等位置前检查l1 >= 4
-
逗号定位错误
- 现象:错误地识别了逗号位置
- 原因:诗句中可能有多个逗号
- 解决:题目似乎只需要第一个逗号,当前实现正确
-
最后一个单词处理不当
- 现象:句号识别错误
- 原因:单词可能包含多个句号
- 解决:使用find('.')而不是直接判断最后一个字符
5.2 测试用例设计
好的测试用例应该包括:
-
常规情况
输入:
1
xiong, xiong xiong.
输出:
qiao ben zhong. -
不符合条件的情况
输入:
1
xiang, xiang xiang.
输出:
Skipped -
边界情况
输入:
1
ong, ong ong.
输出:
qiao ben zhong. -
最小长度测试
输入:
1
a, b ong.
输出:
Skipped (因为逗号前不足3个字符) -
多个逗号情况
输入:
1
ong, abc, def ong.
输出:
qiao ben zhong.
5.3 调试技巧分享
-
打印中间结果
- 在关键步骤后打印vector内容
- 输出逗号位置和检查结果
-
使用断言
- 对关键假设使用assert验证
- 例如assert(l1 >= 4 && l2 >= 4)
-
分步测试
- 先单独测试诗句读取功能
- 再测试逗号定位功能
- 最后测试整体逻辑
-
使用调试器
- 设置断点观察变量值
- 单步执行验证逻辑流程
在实际编程竞赛中,这类字符串处理题目非常常见。掌握这类题目的解题技巧,关键在于:
- 仔细阅读题目,明确所有边界条件
- 设计清晰的算法流程,最好先写伪代码
- 注意字符串索引和边界检查
- 编写足够的测试用例验证各种情况
- 保持代码整洁,方便调试和修改