信息学奥赛入门教程
string 类型 (推荐))char[]'\0' 结尾标记结束)C++ string 通常内部包含指向实际字符数据的指针、当前长度和容量信息。C 风格字符串就是一块连续内存。
(内部管理长度和容量)
(必须以 '\0' 结尾)
C++ string 类型提供了多种灵活的初始化方式:
string s1;string s2 = "Hello"; 或 string s3("World");string 对象复制。 string s4 = s2;string s5(5, 'A'); // s5 为 "AAAAA"string 的一部分创建。 string s6(s2, 1, 3); // s6 为 "ell" (从s2下标1开始,长度3)cin >> stringgetline(cin, string)'\n'。重要提示:混合使用的陷阱
如果在 cin >> var; 之后立即使用 getline(cin, str);,getline 可能会读取到之前 cin 遗留的换行符,导致读入一个空行。
解决方案:在 cin 之后、getline 之前,使用 cin.ignore(); 来清除缓冲区中残留的换行符。
int num;
string line;
cin >> num; // 输入数字后按回车,'\n' 留在缓冲区
cin.ignore(); // 消耗掉那个换行符
getline(cin, line); // 现在可以正常读取下一行了
模拟输入 "Hello World" 后,观察 cin 和 getline 的不同行为。
使用 cout << string 即可方便地输出整个字符串内容。
string greeting = "你好,世界!";
cout << greeting << endl; // 输出:你好,世界!
s.length() 或 s.size(): 返回字符串的字符数 (无'\0')。两者等价。s.empty(): 检查字符串是否为空。返回 true 或 false。s[i]: 访问下标为 i 的字符 (类似数组,不进行边界检查)。s.at(i): 访问下标为 i 的字符 (进行边界检查,越界会抛出异常)。s.front(): 访问第一个字符 (等价于 s[0])。s.back(): 访问最后一个字符 (等价于 s[s.length()-1])。s1 + s2: 返回一个新的字符串,是 s1 和 s2 的连接。s1 += s2: 将 s2 追加到 s1 的末尾 (修改 s1)。s.append(str): 功能类似 +=,有更多重载形式。==, !=, <, >, <=, >=: 直接使用比较运算符,按字典序比较。s1.compare(s2): 返回一个整数。0 表示相等,正数表示 s1 > s2,负数表示 s1 < s2。s.insert(pos, str): 在下标 pos 处插入字符串 str。s.erase(pos, len): 从下标 pos 开始删除 len 个字符。s.replace(pos, len, str): 将从 pos 开始的 len 个字符替换为 str。s.clear(): 清空字符串,使其变为空串。s.find(sub): 查找子串 sub 首次出现的位置(下标)。找不到返回 string::npos。s.rfind(sub): 从右向左查找子串 sub 首次出现的位置。s.find_first_of(chars): 查找 chars 中任意字符首次出现的位置。s.find_last_of(chars): 查找 chars 中任意字符最后出现的位置。s.substr(pos, len): 返回从下标 pos 开始,长度为 len 的子串 (不修改原串)。当前字符串:
字符串本质上是一个字符序列,我们可以像遍历数组一样访问它的每个字符。这使得字符串可以与序列处理模型(如队列、栈)很好地结合。
队列 (Queue) 是一种先进先出 (FIFO - First In, First Out) 的数据结构,非常适合模拟按顺序处理任务或数据的场景。
核心操作:
push(element): 在队尾加入一个元素。pop(): 移除队头元素。front(): 获取队头元素(不移除)。back(): 获取队尾元素(不移除)。empty(): 判断队列是否为空。size(): 获取队列中元素个数。在 C++ 中,使用 #include 并声明 queue 来创建一个字符队列。
回文串是指正读和反读都一样的字符串(例如 "level", "madam")。我们可以用队列和栈(或仅用双端队列/双指针)来判断。
基于队列的思路(与栈配合):
简化思路(仅用队列,演示出队过程): 虽然判断回文通常不用纯队列,但我们可以用队列模拟字符的顺序处理过程。
字符串处理是信息学竞赛中的常见题型。以下是一些基础应用示例:
题意:给定一个字符串和一个字符,统计该字符在字符串中出现的次数。
思路:遍历字符串的每一个字符,如果当前字符与目标字符相同,则计数器加一。
核心代码逻辑:
int count = 0;
for (char c : str) { // C++11 范围for循环
if (c == target_char) {
count++;
}
}
// 或者使用传统 for 循环
for (int i = 0; i < str.length(); ++i) {
if (str[i] == target_char) {
count++;
}
}
题意:判断一个给定的字符串是否为回文串。
思路 1 (双指针):设置两个指针,一个指向字符串开头 (left = 0),一个指向末尾 (right = s.length() - 1)。同步向中间移动,比较 s[left] 和 s[right] 是否相等。如果不等,则不是回文串。如果指针相遇或交错,则是回文串。
思路 2 (反转比较):创建一个原字符串的副本,将其反转,然后比较反转后的字符串是否与原字符串相等。
双指针法演示 (字符串 "level"):
比较 s[0] 和 s[4]
题意:给定一个主字符串 s、一个待查找子串 old_sub 和一个替换子串 new_sub,将 s 中所有出现的 old_sub 替换为 new_sub。
思路:反复使用 s.find(old_sub, start_pos) 查找 old_sub 的位置。如果找到 (返回值不是 string::npos),则使用 s.replace(pos, old_sub.length(), new_sub) 进行替换。注意更新下一次查找的起始位置 start_pos,防止无限循环(如果 new_sub 包含 old_sub)。
核心代码逻辑:
size_t pos = 0; // size_t 是无符号整数类型,常用于表示大小和索引
while ((pos = s.find(old_sub, pos)) != string::npos) {
s.replace(pos, old_sub.length(), new_sub);
pos += new_sub.length(); // 更新下次查找的起始位置
}
string vs char[]string 类更安全、方便,自动管理内存,功能丰富。优先使用 string。
cin 读到空白停止,getline 读整行。注意混合使用时的换行符问题 (cin.ignore())。
掌握长度(size), 访问([], at), 拼接(+, +=), 比较(==, <), 查找(find), 子串(substr), 修改(insert, erase, replace)。
字符串可视为字符序列,能与队列、栈等数据结构结合,实现复杂逻辑。
遍历 (for 循环, 范围 for), 双指针 (回文判断), 查找替换模式。
string::nposfind 等函数找不到目标时返回的特殊值,用于判断是否查找成功。
string::npos) 等情况要考虑周全。