当前位置: 首页 > news >正文

【C++】字符串中的字母反转算法详解

引言

在处理字符串时,我们经常需要根据特定条件对字符串中的字符进行操作。今天,我们将探讨一个有趣的问题:只反转字符串中的字母字符,而非字母字符保持原位。这个问题看似简单,但蕴含着双指针算法的精髓。

目录

引言

问题描述

算法实现

算法解析

1. 双指针技术

2. 算法步骤

3. 边界条件处理

字符判断函数详解

算法优化

1. 使用标准库函数

2. 使用位运算加速

3. 添加内联优化

时间复杂度与空间复杂度分析

时间复杂度:O(n)

空间复杂度:O(1)

变种问题

1. 只反转数字

2. 只反转元音字母

3. 保留特定模式

实际应用场景

测试用例设计

常见错误与注意事项

扩展:Unicode支持

总结


问题描述

给定一个字符串s,要求反转字符串中所有字母字符的位置,但保持非字母字符在原位。

示例 1:

text

输入:s = "ab-cd" 输出:"dc-ba" 解释:字母'a'和'b'、'c'和'd'交换位置,连字符'-'保持原位

示例 2:

text

输入:s = "a-bC-dEf-ghIj" 输出:"j-Ih-gfE-dCba"

示例 3:

text

输入:s = "Test1ng-Leet=code-Q!" 输出:"Qedo1ct-eeLg=ntse-T!"

算法实现

cpp

class Solution { public: // 判断字符是否为字母 bool isLetter(char ch) { if(ch >= 'a' && ch <= 'z') return true; if(ch >= 'A' && ch <= 'Z') return true; return false; } // 只反转字符串中的字母 string reverseOnlyLetters(string s) { int left = 0, right = s.size() - 1; while(left < right) { // 从左向右找到第一个字母 while(left < right && !isLetter(s[left])) { left++; } // 从右向左找到第一个字母 while(left < right && !isLetter(s[right])) { right--; } // 交换两个字母 swap(s[left++], s[right--]); } return s; } };

算法解析

1. 双指针技术

这是典型的双指针(对撞指针)技术:

  • left指针从字符串开头向右移动

  • right指针从字符串末尾向左移动

  • 两个指针相向而行,直到相遇或交错

2. 算法步骤

text

初始状态:s = "a-bC-dEf-ghIj" left = 0, right = 12 第1轮: 跳过非字母:left指向0('a'),right指向12('j') 交换:s[0]和s[12]交换 → s = "j-bC-dEf-ghIa" left++ → 1, right-- → 11 第2轮: 跳过非字母:left指向2('b'),right指向10('h') 交换:s[2]和s[10]交换 → s = "j-hC-dEf-gbIa" left++ → 3, right-- → 9 第3轮: 跳过非字母:left指向4('C'),right指向8('g') 交换:s[4]和s[8]交换 → s = "j-hg-dEf-CbIa" left++ → 5, right-- → 7 第4轮: 跳过非字母:left指向6('E'),right指向6('E')(相同位置) 交换:s[6]和s[6]交换(实际不变) left++ → 7, right-- → 5 结束循环(left > right) 最终结果:"j-Ih-gfE-dCba"

3. 边界条件处理

  • 空字符串或单字符字符串:直接返回原字符串

  • 全是非字母字符:不会执行任何交换

  • 全是字母字符:完全反转整个字符串

字符判断函数详解

cpp

bool isLetter(char ch) { // 方法1:使用字符范围判断(当前实现) if(ch >= 'a' && ch <= 'z') return true; if(ch >= 'A' && ch <= 'Z') return true; return false; // 方法2:使用C标准库函数 // return isalpha(ch); // 方法3:使用ASCII码特性 // return (ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122); }

ASCII码相关知识:

  • 大写字母:A-Z → 65-90

  • 小写字母:a-z → 97-122

  • 数字:0-9 → 48-57

算法优化

1. 使用标准库函数

cpp

bool isLetter(char ch) { return isalpha(ch); // C标准库函数,可识别本地化字符集 }

2. 使用位运算加速

利用ASCII码的特性,字母的ASCII码与32(空格)进行或运算会得到小写形式:

cpp

bool isLetter(char ch) { // 转换为小写字母 char lower = ch | 32; // 判断是否为a-z return lower >= 'a' && lower <= 'z'; }

3. 添加内联优化

cpp

class Solution { public: // 内联函数提高效率 inline bool isLetter(char ch) { return isalpha(ch); } string reverseOnlyLetters(string s) { // 算法主体不变 // ... } };

时间复杂度与空间复杂度分析

时间复杂度:O(n)

  • 每个字符最多被访问两次(一次由左指针,一次由右指针)

  • 总操作次数不超过2n,其中n是字符串长度

空间复杂度:O(1)

  • 只使用了常数个额外变量(left, right)

  • 原地修改,不需要额外存储空间

变种问题

1. 只反转数字

cpp

string reverseOnlyDigits(string s) { int left = 0, right = s.size() - 1; while(left < right) { while(left < right && !isdigit(s[left])) left++; while(left < right && !isdigit(s[right])) right--; swap(s[left++], s[right--]); } return s; }

2. 只反转元音字母

cpp

bool isVowel(char ch) { ch = tolower(ch); return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u'; } string reverseOnlyVowels(string s) { int left = 0, right = s.size() - 1; while(left < right) { while(left < right && !isVowel(s[left])) left++; while(left < right && !isVowel(s[right])) right--; swap(s[left++], s[right--]); } return s; }

3. 保留特定模式

cpp

// 反转字符串,但保持连字符分隔的单词内部顺序 // 例如:"hello-world" -> "dlrow-olleh" string reverseWithSeparator(string s, char separator) { // 整体反转 reverse(s.begin(), s.end()); // 对每个分隔部分单独反转 int start = 0; for(int i = 0; i <= s.size(); i++) { if(i == s.size() || s[i] == separator) { reverse(s.begin() + start, s.begin() + i); start = i + 1; } } return s; }

实际应用场景

  1. 文本处理:在编辑器中实现选择性反转

  2. 数据加密:对特定字符进行位置变换

  3. 字符串规范化:在保持格式的同时重新排列内容

  4. 代码混淆:对标识符中的字母进行反转

测试用例设计

全面的测试应该包括:

cpp

void test() { Solution sol; // 正常情况 assert(sol.reverseOnlyLetters("ab-cd") == "dc-ba"); assert(sol.reverseOnlyLetters("a-bC-dEf-ghIj") == "j-Ih-gfE-dCba"); assert(sol.reverseOnlyLetters("Test1ng-Leet=code-Q!") == "Qedo1ct-eeLg=ntse-T!"); // 边界情况 assert(sol.reverseOnlyLetters("") == ""); assert(sol.reverseOnlyLetters("a") == "a"); assert(sol.reverseOnlyLetters("123") == "123"); assert(sol.reverseOnlyLetters("!@#$") == "!@#$"); // 全字母情况 assert(sol.reverseOnlyLetters("abcdef") == "fedcba"); // 混合情况 assert(sol.reverseOnlyLetters("AaBbCc") == "cCbBaA"); assert(sol.reverseOnlyLetters("1a2b3c4d") == "1d2c3b4a"); }

常见错误与注意事项

  1. 指针越界:确保while循环条件中包含left < right

  2. 字符编码:考虑不同的字符编码(ASCII vs Unicode)

  3. 性能考虑:避免在循环中重复计算s.size()

  4. 可读性:适当添加注释,特别是边界条件的处理

扩展:Unicode支持

对于Unicode字符,需要使用宽字符和相应的库函数:

cpp

#include <cwctype> // 用于宽字符判断 wstring reverseOnlyLettersUnicode(wstring s) { int left = 0, right = s.size() - 1; while(left < right) { while(left < right && !iswalpha(s[left])) left++; while(left < right && !iswalpha(s[right])) right--; swap(s[left++], s[right--]); } return s; }

总结

这个字符串字母反转问题展示了双指针算法在字符串处理中的强大应用。通过这个算法,我们学习了:

  1. 双指针技巧:如何有效地同时从两端遍历和处理数据

  2. 字符分类:如何判断字符的类型(字母、数字、符号等)

  3. 原地修改:如何在不需要额外空间的情况下修改字符串

  4. 边界处理:如何正确处理各种边界情况

关键要点

  • 使用双指针从两端向中间遍历

  • 跳过非目标字符,只处理符合条件的字符

  • 确保指针不会越界

  • 考虑各种边界情况和特殊输入

这个算法简单而高效,是面试中常见的题目,也是实际开发中处理字符串问题的有用工具。掌握这种模式,可以帮助我们解决许多类似的字符串操作问题。

http://www.jsqmd.com/news/497011/

相关文章:

  • CLIP-GmP-ViT-L-14实战教程:集成至LangChain实现多模态RAG检索
  • IndexTTS-2-LLM医疗语音系统:病历朗读服务部署实战
  • SmolVLA在低成本机器人中的应用:视觉-语言-动作闭环落地实践
  • MCP自定义服务器应用研究;langchain中对话模型实例;
  • 海外展会营销推广平台推荐,搭配Google、Facebook、TikTok、ins、LinkedIn等助力企业链接海外精准客户 - 品牌2026
  • embeddinggemma-300m效果展示:开源LLM技术博客语义导航与知识图谱构建案例
  • Maven build配置
  • 深求·墨鉴效果展示:水墨‘笔触留痕’功能直观验证AI识别逻辑可靠性
  • 浦语灵笔2.5-7B惊艳效果:同一张图多轮提问(物体→关系→推理→建议)
  • 前瞻2026:三河市玻璃抛光服务商全景解析与选型指南 - 2026年企业推荐榜
  • DAMO-YOLO手机检测WebUI电子围栏:指定区域检测开关配置教程
  • MogFace人脸检测模型-WebUI案例实录:从模糊证件照中成功提取全部人脸ROI区域
  • Qwen2-VL-2B-Instruct应用落地:跨境电商多语言SKU描述与主图匹配校验
  • mT5中文-base零样本增强模型开源大模型部署:中小企业低成本NLP数据增强方案
  • CLIP-GmP-ViT-L-14应用案例:工业零件图-技术规格书语义检索系统
  • 2026北京石雕采购风向标:五大口碑直销厂商实力横评与选型攻略 - 2026年企业推荐榜
  • UI-TARS-desktop参数详解:vLLM推理配置+Qwen3-4B-Instruct多工具调用实战
  • MedGemma-X性能调优:调整batch_size与max_new_tokens平衡速度与质量
  • ccmusic-database应用场景:AI DJ系统——根据当前曲目流派自动混搭下一首候选曲
  • STEP3-VL-10B开源大模型教程:GitHub源码编译+HuggingFace模型加载全流程
  • RetinaFace开源模型部署:免编译、免依赖、预装OpenCV+PIL+NumPy全栈
  • 文脉定序多场景落地:法律、医疗、教育领域语义重排序应用案例集
  • C语言、循环结构
  • JavaWeb(后端)
  • 海外社媒营销服务商合集,Facebook、LinkedIn、TikTok代运营,适配多品类B2B外贸需求 - 品牌2026
  • 2026年河南单反相机回收公司推荐:数码相机/CCD/镜头/无人机/鼠标回收服务商 - 品牌推荐官
  • Z-Image-Turbo_Sugar脸部Lora效果展示:同一人物多角度(正脸/侧脸/45°)生成一致性
  • Janus-Pro-7B训练数据揭秘:9000万条多模态样本如何提升稳定性与泛化性
  • Audio Pixel Studio人声分离原理浅析:基于频谱分析的轻量化UVR实现路径
  • C++成员模板类