C++14 string基本用法 string复习参考
在竞赛中,string 的优势在于极其方便的拼接、比较和 STL 联动;但它的劣势在于底层涉及动态内存分配,一旦用错极容易造成 TLE (超时) 或 RE (运行错误)。
一、 初始化与 I/O
1. 极速初始化
string s; // 空串
string s1 = "abc"; // 直接赋值
string s2(5, 'a'); // "aaaaa" (构造长度为 5 的相同字符,极常用)
string s3(s1, 1, 2); // 截取 s1 从下标 1 开始,长度为 2 的部分 ("bc")
2. 竞赛中的读取策略
cin >> s; // 读入,遇到空格、回车、Tab 停止
getline(cin, s); // 读入一整行,包含空格,遇到回车停止
爆零坑点(getline 吞回车):
如果前面有 cin >> n;,必须加一句 cin.get(); 或 cin.ignore(); 吃掉缓冲区遗留的回车符,否则 getline 会读入一个空串直接结束!
二、 核心操作 (极简且极速)
1. 长度与容量
int len = s.length(); // 或 s.size(),两者等价,C++11 起强制 O(1) 复杂度
bool empty = s.empty(); // 判断是否为空串,比 s.size() == 0 更优雅
2. 元素访问
char c1 = s[i]; // O(1) 访问,竞赛推荐,不作越界检查(极速)
char c2 = s.at(i); // O(1) 访问,会做越界检查并抛出异常(略慢,不推荐)
char head = s.front(); // 获取首字符 (C++11)
char tail = s.back(); // 获取尾字符 (C++11)
3. 追加与删除 (尾部操作)
s += 'a'; // 追加单个字符,均摊 O(1)
s += "abc"; // 追加字符串
s.push_back('a'); // 同 s += 'a'
s.pop_back(); // 极速删掉最后一个字符 (C++11)
三、 高频算法函数
1. 截取子串 substr
// s.substr(起始下标, 截取长度)
string sub = s.substr(2, 3); // 从下标 2 开始,截取 3 个字符
string sub2 = s.substr(2); // 从下标 2 开始,一直截取到末尾
超时警告: substr 的复杂度是 O(L)(L 为截取长度),并且会产生全新的内存拷贝。绝对不要在多重循环里高频调用 substr 提取子串来比较(比如咱们做字符串哈希或 KMP 时,老老实实用下标或哈希值,千万别用 substr 切片比较)。
2. 查找子串 find
// 返回匹配到的第一个首字母下标
int pos = s.find("abc");
int pos2 = s.find("abc", 2); // 从下标 2 开始往后找
// 找不到时,返回 string::npos
if (s.find("abc") == string::npos) {
cout << "Not Found";
}
性能提示: std::string::find 的最坏复杂度是。虽然 STL 内部有优化,但在面对毒瘤出题人专门构造的卡常数据时(如
aaaaa...b),请自觉换用 KMP。
3. 增删改 (中间操作)
s.insert(2, "xyz"); // 在下标 2 处插入 "xyz"
s.erase(2, 3); // 从下标 2 开始,删掉 3 个字符
s.replace(2, 3, "xyz"); // 把从下标 2 开始的 3 个字符替换为 "xyz"
性能提示: 这三个操作的复杂度全部是O(N),因为需要大面积移动内存。在百万级数据的题中,慎用。
四、 STL 算法库无缝联动
string 本质上是一个 vector<char>,可以直接套用 <algorithm> 库:
#include <algorithm>
reverse(s.begin(), s.end()); // 字符串翻转 O(N)
sort(s.begin(), s.end()); // 字符升序排序 O(N log N)
// 字符去重 (配合 erase 缩容)
s.erase(unique(s.begin(), s.end()), s.end());
// 下一个字典序全排列
next_permutation(s.begin(), s.end());
五、 类型转换 (C++11/14 神器)
省去了以前写 stringstream 极其痛苦的转换过程:
// 数字转 string
string s_num = to_string(123456);
// string 转数字
int num = stoi(s); // string -> int
long long llnum = stoll(s); // string -> long long
double dnum = stod(s); // string -> double
六、 考场三大铁律
这三条铁律学生要刻在骨子里,这是无数 OIer 用眼泪换来的:
铁律 1:无符号整型的减法惨案 (s.size() - 1)
s.size() 的返回值类型是 size_t(无符号整型)。
如果此时字符串是空串,s.size() 为 0。
你在循环里写 for(int i = 0; i < s.size() - 1; i++)。
发生什么? 0 - 1 在无符号整型下会下溢出,变成 18446744073709551615。这个循环会变成死循环,直接导致数组越界,稳稳吃一个 RE / TLE!
✅ 解法: 永远写成 i + 1 < s.size() 或者强转 i < (int)s.size() - 1。
铁律 2:+= 与 + 的天壤之别
s = s + 'a'; // ❌ 极其致命的错误!
s += 'a'; // ✅ 正确做法
s = s + 'a' 会产生一个临时的拷贝对象,申请内存、复制一遍原串,再加一个字符。如果放在循环里,复杂度直接从O(N)飙升到O(N^2)。
s += 'a' 则是利用内部的容量翻倍机制在尾部直接拼接,均摊复杂度是绝对的O(1)。
铁律 3:函数传参必须带引用 &
void dfs(string s) // ❌ 每次递归都会把整个字符串 O(N) 复制一遍,瞬间 MLE/TLE!
void dfs(const string& s) // ✅ 只传递地址,O(1) 开销!
更多推荐


所有评论(0)