信息学奥赛入门教程
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 >> string
getline(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::npos
find
等函数找不到目标时返回的特殊值,用于判断是否查找成功。
string::npos
) 等情况要考虑周全。