C++中String类常见题目分享

发布时间:2026/11/4 18:18:50
C++中String类常见题目分享 前言std::string相关的题目是笔试和面试的高频题型反转单词、判断回文、找第一个不重复字符、字符串转整数、去除空白、按分隔符切分。这些题单个看都不难但每个都有边界条件写错一处就会在隐藏用例上失败。常见的共性问题有三个。第一用std::string::npos时直接和int比较或者把npos当成 -1 用npos是size_type的最大值和int比较会发生符号转换。第二处理字符分类函数std::isalnum、std::isdigit、std::tolower时直接传char遇到负值就是未定义行为undefined behavior, UB。第三字符串转整数时没有处理溢出和前导空白或者把、-的位置判断错。本文挑五类常见题目每类给出一个 C17 的实现和边界说明。代码都只用标准库不依赖特定平台。一、反转字符串与反转单词最简单的「反转字符串」是原地交换首尾字符// 适用于 C17#include string#include utilityvoid reverseString(std::string s) {std::size_t i 0;std::size_t j s.size();while (i j) {--j;if (i j) break;std::swap(s[i], s[j]);i;}}j初始为size()先--j再交换避免访问s[size()]那个位置是末尾空字符虽然可读但不应该被交换。i j的判断处理了长度为偶数和奇数两种情况。「反转单词」要求把the sky is blue变成blue is sky the并且把多个连续空格压缩成一个。用std::istringstream按空白切分最省事// 适用于 C17#include sstream#include string#include vectorstd::string reverseWords(const std::string s) {std::istringstream iss(s);std::vectorstd::string words;std::string w;while (iss w) {words.push_back(w);}std::string result;for (auto it words.rbegin(); it ! words.rend(); it) {if (!result.empty()) result ;result *it;}return result;}iss w会跳过前导空白、在下一个空白处停止所以连续多个空格不会产生空单词。返回的字符串没有前导或尾随空格。时间 O(n)空间 O(n)。如果要求原地反转O(1) 额外空间做法是「先整体反转再逐个单词反转」同时处理多余空格。那种写法边界较多本文从略面试时要先问清楚是否允许额外空间。二、回文判断判断一个字符串是否为回文可以用双指针从两端向中间比较。如果忽略大小写和非字母数字字符需要先跳过它们// 适用于 C17#include cctype#include stringbool isPalindrome(const std::string s) {std::size_t i 0;std::size_t j s.size();while (i j) {while (i j std::isalnum(static_castunsigned char(s[i])) 0) {i;}while (i j std::isalnum(static_castunsigned char(s[j - 1])) 0) {--j;}if (i j) {const unsigned char a static_castunsigned char(s[i]);const unsigned char b static_castunsigned char(s[j - 1]);if (std::tolower(a) ! std::tolower(b)) return false;i;--j;}}return true;}关键点std::isalnum和std::tolower的参数必须是unsigned char提升后的值或EOF。直接传char如果char有符号且字符值大于 127会变成负数行为未定义。j从size()开始比较s[j-1]这样不会越界。跳过非字母数字后要重新判断i j否则可能已经把两个指针走到交叉。空字符串和只含非字母数字的字符串都返回true这符合「忽略这些字符后是回文」的语义如果题目要求空串返回false需要单独处理。三、第一个只出现一次的字符统计每个字符出现的次数再按原顺序找第一个计数为 1 的字符。字符集是char时用 256 个桶的数组即可// 适用于 C17#include array#include cstddef#include stringint firstUniqChar(const std::string s) {std::arrayint, 256 count{};for (char c : s) {count[static_castunsigned char(c)];}for (std::size_t i 0; i s.size(); i) {if (count[static_castunsigned char(s[i])] 1) {return static_castint(i);}}return -1;}std::arrayint, 256 count{};的值初始化会把所有元素置 0。下标用unsigned char转换保证落在 0 到 255。如果字符集是 Unicodechar的 256 个桶就不够了应该按 UTF-8 字节序列或码点处理通常需要额外的库。本文只讨论单字节字符集。如果要求「第一个不重复的字符」而不是下标把返回值改成char找不到时返回\0或抛异常并在文档里写清楚。四、字符串转整数实现一个类似std::stoi的函数要求跳过前导空白。可选的正负号。读取连续数字遇到非数字停止。超出int范围时返回INT_MAX或INT_MIN。没有数字时返回 0。// 适用于 C17#include cctype#include climits#include stringint myAtoi(const std::string s) {std::size_t i 0;while (i s.size() s[i] ) i;int sign 1;if (i s.size() (s[i] || s[i] -)) {if (s[i] -) sign -1;i;}long long value 0;while (i s.size() std::isdigit(static_castunsigned char(s[i])) ! 0) {const int digit s[i] - 0;value value * 10 digit;if (value 2147483648LL) {value 2147483648LL;break;}i;}if (sign 1 value 2147483647LL) return INT_MAX;if (sign -1 value 2147483648LL) return INT_MIN;return static_castint(sign * value);}边界说明只跳过空格 不跳过制表符和换行题目如果要求所有空白改用std::isspace。2147483648LL是INT_MAX 1用long long表示避免int溢出。一旦value超过2147483648就截断并跳出循环防止value * 10在超长输入上溢出long long。正数上限是2147483647负数下限是-2147483648两者不对称所以判断条件分开写。没有数字时value为 0返回 0符合常见约定。std::stoi的行为不同它在无法转换时抛std::invalid_argument越界时抛std::out_of_range。如果只是解析已知合法的输入用std::stoi更省事如果要实现题目要求的「饱和转换」就自己写。五、去除首尾空白与按分隔符切分去空白可以用find_first_not_of和find_last_not_of// 适用于 C17#include stringstd::string trim(const std::string s) {const std::string whitespace \t\n\r\f\v;const std::size_t begin s.find_first_not_of(whitespace);if (begin std::string::npos) return ;const std::size_t end s.find_last_not_of(whitespace);return s.substr(begin, end - begin 1);}find_first_not_of返回第一个不是空白字符的下标如果全是空白返回npos此时直接返回空串。find_last_not_of返回最后一个非空白字符的下标substr的长度是end - begin 1。按单个分隔符切分// 适用于 C17#include string#include vectorstd::vectorstd::string split(const std::string s, char delim) {std::vectorstd::string parts;std::string current;for (char c : s) {if (c delim) {parts.push_back(current);current.clear();} else {current c;}}parts.push_back(current);return parts;}这个实现会保留空字段a,,b切成{a, , b}a,切成{a, }。这通常是 CSV 解析想要的语义如果不想要空字段在push_back前判断!current.empty()但要注意这样会改变「连续分隔符」的含义。split每次current c可能导致多次重新分配。对长字符串可以先reserve或者在确定分隔符位置后用substr。对一般规模现代std::string的扩容策略已经足够。六、完整示例// 适用于 C17#include iostream#include string#include vectorstd::string trim(const std::string s) {const std::string whitespace \t\n\r\f\v;const std::size_t begin s.find_first_not_of(whitespace);if (begin std::string::npos) return ;const std::size_t end s.find_last_not_of(whitespace);return s.substr(begin, end - begin 1);}std::vectorstd::string split(const std::string s, char delim) {std::vectorstd::string parts;std::string current;for (char c : s) {if (c delim) {parts.push_back(current);current.clear();} else {current c;}}parts.push_back(current);return parts;}int main() {std::cout [ trim( hello world ) ]\n;std::cout [ trim( ) ]\n;for (const auto part : split(a,b,,c, ,)) {std::cout [ part ] ;}std::cout \n;return 0;}按标准语义trim( hello world )得到[hello world]trim( )得到[]split(a,b,,c, ,)得到[a] [b] [] [c]。常见坑点1. 字符分类函数直接传 char❌std::isalnum(s[i])当char有符号且字符值大于 127 时是 UB。✅std::isalnum(static_castunsigned char(s[i]))。2. 把 npos 当成 -1❌int pos s.find(x); if (pos -1) ...npos转成int可能是 -1也可能是其他值行为依赖实现。✅ 用std::string::npos比较类型用std::string::size_type或auto。3. 回文双指针越界❌std::size_t j s.size() - 1;对空串下溢成最大值。✅j s.size()访问s[j - 1]或先判断空串。4. 反转单词时保留多余空格❌ 直接按空格切分后拼接多个空格产生空单词结果里出现连续空格。✅ 用istringstream 自动跳过空白或手动压缩空格。5. 字符串转整数溢出❌int value value * 10 digit;超长输入时溢出UB。✅ 用long long累加超过INT_MAX 1就截断。6. 忘记处理正负号❌ 只判断-把当成非数字返回 0。✅ 同时接受和-并确保符号后至少有一位数字。7. split 对空字符串返回空 vector❌split(, ,)返回{}调用方按「至少一个字段」处理时越界。✅ 本文实现返回{}明确文档语义或按需过滤。8. 在 range-for 中修改字符串❌for (char c : s) { if (c ) s.erase(...); }迭代器失效。✅ 用下标 erase的正确循环或 erase-remove 惯用法。总结题目核心方法关键边界反转字符串双指针交换奇偶长度、空串反转单词按空白切分后逆序拼接多余空格回文判断双指针 跳过非字母数字unsigned char、指针交叉第一个唯一字符计数数组字符集大小、返回值约定字符串转整数逐位累加 饱和溢出、符号、前导空白trimfind_first/last_not_of全空白split遍历 累积空字段语义字符串题目的共同点是「边界比算法多」。写之前先把输入输出约定列清楚空串怎么处理、是否忽略大小写、是否保留空字段、溢出时返回什么。把这些约定写进注释再动手实现比写完再补边界要可靠得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询