第 38 课:字符串模拟
字符串模拟就是根据具体的问题需求,利用编程提供的字符串相关操作(如获取字符、拼接、截取、查找、替换等),来模拟题目要求的某些过程,从而解决问题。
38.1 常见应用场景
文本处理:比如要求统计文章中某个单词出现的次数,可以通过遍历文章,逐个检查单词来实现。
游戏开发:比如模拟一个冒险游戏,玩家输入指令(字符串形式),程序根据指令对游戏场景(用字符串描述)进行相应的改变,如进入新的房间、触发事件等。
小程序制作:比如制作一个文本编辑器,里面涉及撤销和重做功能,可以将每次的编辑操作记录为字符串,然后根据撤销或重做的指令来恢复或再次执行相应的操作。
字符串模拟还有更多的应用等待着你去挖掘,让我们一起来学习它的相关操作吧!
38.2 字符串常用函数
insert 插入函数
格式:s1.insert(pos, s2);
功能:在字符串 s1 中指定下标 pos 的位置插入另一个字符串 s2。
string a = "haha", b = "123";
a.insert(2, b);
cout << a;出现的结果是:ha123ha
erase 删除函数
格式:s1.erase(pos, len);
功能:删除字符串 s1 中从 pos 下标开始的 len 个长度的字符。
string a = "hello";
a.erase(2, 3);
cout << a;出现的结果是:he
substr 截取子串函数
格式:s1.substr(pos, len);
功能:从字符串 s1 中 pos 下标开始截取之后 len 个长度的字符,得到一个新的字符串常量。
string a = "hello", b;
b = a.substr(2, 3);
cout << b;出现的结果是:llo
find 查找函数
格式:s1.find(s2, pos);
功能:从字符串 s1 中指定下标所对应的元素起,查找字符串 s2 第一次出现的位置,若找到则返回第一次出现的下标,若找不到,则返回 -1(要将返回值赋值给 int 型变量)。
string a = "haha", b = "ha";
int n = a.find(b, 0);
cout << n;出现的结果是:0,表示 0 号位置能找到字符串 ha
以上这些函数均需添加 string 头文件,即 #include <string>。
38.3 实例讲解
例 1:填空
在一个英文句子中,有一组括号中间加一个空格 ( ),需要在其中填写对应的单词,如果填写单词 happy,会变成 (happy)。
现在给出未填写单词的句子和需要填写的单词,请把单词填写进去。
【输入格式】输入两行。
第 1 行输入一个英文句子,有空格,有括号。
第 2 行输入需要填入的单词。
【输出格式】输出一行,为单词填写进去的句子。
【输入样例】
we are a ( ) team
happy【输出样例】
we are a (happy) team解析
- 输入处理:输入一个句子(字符串),但由于句子可能包含空格,所以不能直接使用 cin 进行输入,因为 cin 遇到空格就停止读取了,但可以使用 getline 以确保完整读取整行句子。操作如下:
getline(cin, a);- 查找占位符位置:使用 find 查找函数从输入的句子中查找括号,即
( )的位置。具体操作如下:
int n = a.find(c, 0);其中第一个参数 c 是要查找的子字符串,第二个参数 0 表示从字符串 a 的索引 0 位置开始查找,如果找到了 ( ),则返回其在 a 中的起始索引,如果未找到,则返回 -1。将找到的索引存储在变量 n 中。
- 删除占位符中的空格:使用 erase 删除函数删除括号中的空格。具体操作如下:
a.erase(n + 1, 1);第一个参数 n + 1 表示删除操作的起始位置,因为 ( ) 中要删除的空格位于 ( 后的第一个位置,所以位置是 n + 1。第二个参数 1 表示要删除的字符数量,这里只需要删除一个空格,所以为 1。
- 插入单词:使用 insert 插入函数在字符串的指定位置插入单词。具体操作如下:
a.insert(n + 1, b);第一个参数 n + 1 表示插入的位置,即原 ( ) 中空格的位置,第二个参数 b 是要插入的字符串,也就是之前输入的需要填入的单词。
参考代码
#include <iostream>
#include <string>
using namespace std;
int main()
{
string a, b, c = "( )";
getline(cin, a);
cin >> b;
int n = a.find(c, 0); // 查找
a.erase(n + 1, 1); // 删除
a.insert(n + 1, b); // 插入
cout << a;
return 0;
}例 2:替换单词
蛙蛙在一个英文句子里发现了一个单词使用有误,需要把这个错误的单词替换为正确的单词。比如在 we make homework 这个句子中,make 是错误的,应该替换成 do。
现在要求你编写代码来实现此功能,即输入一个英文句子、错误的单词以及正确的单词,输出替换后的英文句子。
【输入格式】输入两行。
第 1 行输入一个英文句子,英文句子中有可能会出现空格。
第 2 行输入两个单词,用空格隔开,分别是句子中需要被替换的单词和新单词。
【输出格式】输出一行,为单词替换之后正确的英文句子。
【输入样例】
we make homework
make do【输出样例】
we do homework解析
- 输入处理:输入一个句子(字符串),但由于句子可能包含空格,所以直接使用 getline 以确保完整读取整行句子。操作如下:
getline(cin, a);- 字符串预处理:定义三个字符串,分别命名为 a、b、c;为了避免在查找和替换单词时出现部分匹配的问题,需要在字符串 a、b 和 c 的前后都添加一个空格。
例如,若句子中有 makeup,而要替换的单词是 make,添加空格后就能确保只匹配完整的 make 而不会错误匹配到 makeup 中的 make,操作如下:
a = " " + a + " ";
b = " " + b + " ";
c = " " + c + " ";- 查找需要替换的单词位置:使用 find 函数在字符串 a 中从索引 0 开始查找字符串 b(需要被替换的错误单词)第一次出现的位置,将该位置的索引存储在变量 n 中。
接着使用 b.size() 获取字符串 b 的长度,将其存储在变量 m 中,以便后续用这个长度来确定要删除的字符数量,操作如下:
int n = a.find(b, 0), m = b.size();- 删除错误单词:使用 erase 函数从字符串 a 的索引 n 位置开始,删除长度为 m 的字符,也就是删除查找到的错误单词。操作如下:
a.erase(n, m);- 插入正确单词:使用 insert 函数在字符串 a 的索引 n 位置插入字符串 c,即把正确的单词插入原来错误单词所在的位置。操作如下:
a.insert(n, c);- 删除预处理添加的前导空格:由于之前在字符串 a 前面添加了一个空格,现在使用 erase 函数从索引 0 位置开始删除 1 个字符,也就是删除这个前导空格。操作如下:
a.erase(0, 1);参考代码
#include <iostream>
#include <string>
using namespace std;
int main()
{
string a, b, c;
getline(cin, a);
cin >> b >> c;
a = " " + a + " ";
b = " " + b + " ";
c = " " + c + " "; // 前后加空格
int n = a.find(b, 0), m = b.size(); // 查找 b 单词的位置
a.erase(n, m); // 删除 b 单词
a.insert(n, c); // 插入 c 单词
a.erase(0, 1); // 删除前面的空格
cout << a;
return 0;
}例 3:回文串
有一种特殊的字符串被称作回文串,回文串是指无论从前往后读,还是从后往前读,呈现的字符顺序都完全一致的字符串。
比如日常常见的 level,从左至右拼读为 l-e-v-e-l,从右至左同样是 l-e-v-e-l;又如 noon,正向与反向的拼写均为 n-o-o-n。它们都是回文串家族中的一员。
现在,氪町博士给定了 n 个各式各样的字符串,要求蛙蛙通过精准的判断逻辑,甄别出这些字符串里哪些属于回文串,哪些并非回文串。
【输入格式】输入 n+1 行,n 是小于 100 的正整数。
第 1 行输入一个整数 n,表示字符串个数。
之后的 n 行,每行一个字符串。
【输出格式】输出 n 行,每行一个答案,如果字符串是回文串,就输出 yes,否则输出 no。
【输入样例】
3
aba
abba
abab【输出样例】
yes
yes
no解析
循环处理每个字符串:先输入 m 个字符串,然后使用 for 循环,循环次数为 m 次,即对每个输入的字符串进行处理。
初始化标志变量:定义一个整型变量 flag 并将其初始化为 1,flag 作为一个标志,用于标记当前字符串是否为回文串,初始值为 1,表示假设该字符串是回文串。
读取当前字符串:依然使用 getline 函数读取一行字符串,并将其存储到变量 a 中。操作如下:
getline(cin, a);- 获取字符串长度:使用 size 函数获取字符串 a 的长度,并将其赋值给变量 n。操作如下:
int n = a.size();- 检查字符串是否为回文串:使用 for 循环遍历字符串的前半部分,循环变量 i 从 0 开始,到 n/2 - 1 结束。
在每次循环中,比较字符串的第 i 个字符和第 n - 1 - i 个字符是否相等,如果不相等,说明该字符串不是回文串,将 flag 置为 0,并使用 break 语句跳出循环。具体操作如下:
for (int i = 0; i < n / 2; i++)
if (a[i] != a[n - 1 - i])
{
flag = 0;
break;
}- 输出判断结果:根据 flag 的值输出判断结果,如果 flag 为 1,说明该字符串是回文串,输出 yes,否则输出 no。操作如下:
if (flag == 1)
cout << "yes" << endl;
else
cout << "no" << endl;参考代码
#include <iostream>
#include <string>
using namespace std;
int main()
{
string a;
int m;
cin >> m;
getline(cin, a);
for (int j = 1; j <= m; j++)
{
int flag = 1;
getline(cin, a);
int n = a.size();
for (int i = 0; i < n / 2; i++)
if (a[i] != a[n - 1 - i]) // 前后对应查找
{
flag = 0; // 发现不是回文串就放下旗子
break;
}
if (flag == 1)
cout << "yes" << endl;
else
cout << "no" << endl;
}
return 0;
}