Skip to content

第 38 课:字符串模拟 ​

字符串模拟就是根据具体的问题需求,利用编程提供的字符串相关操作(如获取字符、拼接、截取、查找、替换等),来模拟题目要求的某些过程,从而解决问题。

38.1 常见应用场景 ​

文本处理:比如要求统计文章中某个单词出现的次数,可以通过遍历文章,逐个检查单词来实现。

游戏开发:比如模拟一个冒险游戏,玩家输入指令(字符串形式),程序根据指令对游戏场景(用字符串描述)进行相应的改变,如进入新的房间、触发事件等。

小程序制作:比如制作一个文本编辑器,里面涉及撤销和重做功能,可以将每次的编辑操作记录为字符串,然后根据撤销或重做的指令来恢复或再次执行相应的操作。

字符串模拟还有更多的应用等待着你去挖掘,让我们一起来学习它的相关操作吧!

38.2 字符串常用函数 ​

insert 插入函数 ​

格式:s1.insert(pos, s2);

功能:在字符串 s1 中指定下标 pos 的位置插入另一个字符串 s2。

cpp
string a = "haha", b = "123";
a.insert(2, b);
cout << a;

出现的结果是:ha123ha

erase 删除函数 ​

格式:s1.erase(pos, len);

功能:删除字符串 s1 中从 pos 下标开始的 len 个长度的字符。

cpp
string a = "hello";
a.erase(2, 3);
cout << a;

出现的结果是:he

substr 截取子串函数 ​

格式:s1.substr(pos, len);

功能:从字符串 s1 中 pos 下标开始截取之后 len 个长度的字符,得到一个新的字符串常量。

cpp
string a = "hello", b;
b = a.substr(2, 3);
cout << b;

出现的结果是:llo

find 查找函数 ​

格式:s1.find(s2, pos);

功能:从字符串 s1 中指定下标所对应的元素起,查找字符串 s2 第一次出现的位置,若找到则返回第一次出现的下标,若找不到,则返回 -1(要将返回值赋值给 int 型变量)。

cpp
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

解析

  1. 输入处理:输入一个句子(字符串),但由于句子可能包含空格,所以不能直接使用 cin 进行输入,因为 cin 遇到空格就停止读取了,但可以使用 getline 以确保完整读取整行句子。操作如下:
cpp
getline(cin, a);
  1. 查找占位符位置:使用 find 查找函数从输入的句子中查找括号,即 ( ) 的位置。具体操作如下:
cpp
int n = a.find(c, 0);

其中第一个参数 c 是要查找的子字符串,第二个参数 0 表示从字符串 a 的索引 0 位置开始查找,如果找到了 ( ),则返回其在 a 中的起始索引,如果未找到,则返回 -1。将找到的索引存储在变量 n 中。

  1. 删除占位符中的空格:使用 erase 删除函数删除括号中的空格。具体操作如下:
cpp
a.erase(n + 1, 1);

第一个参数 n + 1 表示删除操作的起始位置,因为 ( ) 中要删除的空格位于 ( 后的第一个位置,所以位置是 n + 1。第二个参数 1 表示要删除的字符数量,这里只需要删除一个空格,所以为 1。

  1. 插入单词:使用 insert 插入函数在字符串的指定位置插入单词。具体操作如下:
cpp
a.insert(n + 1, b);

第一个参数 n + 1 表示插入的位置,即原 ( ) 中空格的位置,第二个参数 b 是要插入的字符串,也就是之前输入的需要填入的单词。

参考代码

cpp
#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

解析

  1. 输入处理:输入一个句子(字符串),但由于句子可能包含空格,所以直接使用 getline 以确保完整读取整行句子。操作如下:
cpp
getline(cin, a);
  1. 字符串预处理:定义三个字符串,分别命名为 a、b、c;为了避免在查找和替换单词时出现部分匹配的问题,需要在字符串 a、b 和 c 的前后都添加一个空格。

例如,若句子中有 makeup,而要替换的单词是 make,添加空格后就能确保只匹配完整的 make 而不会错误匹配到 makeup 中的 make,操作如下:

cpp
a = " " + a + " ";
b = " " + b + " ";
c = " " + c + " ";
  1. 查找需要替换的单词位置:使用 find 函数在字符串 a 中从索引 0 开始查找字符串 b(需要被替换的错误单词)第一次出现的位置,将该位置的索引存储在变量 n 中。

接着使用 b.size() 获取字符串 b 的长度,将其存储在变量 m 中,以便后续用这个长度来确定要删除的字符数量,操作如下:

cpp
int n = a.find(b, 0), m = b.size();
  1. 删除错误单词:使用 erase 函数从字符串 a 的索引 n 位置开始,删除长度为 m 的字符,也就是删除查找到的错误单词。操作如下:
cpp
a.erase(n, m);
  1. 插入正确单词:使用 insert 函数在字符串 a 的索引 n 位置插入字符串 c,即把正确的单词插入原来错误单词所在的位置。操作如下:
cpp
a.insert(n, c);
  1. 删除预处理添加的前导空格:由于之前在字符串 a 前面添加了一个空格,现在使用 erase 函数从索引 0 位置开始删除 1 个字符,也就是删除这个前导空格。操作如下:
cpp
a.erase(0, 1);

参考代码

cpp
#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

解析

  1. 循环处理每个字符串:先输入 m 个字符串,然后使用 for 循环,循环次数为 m 次,即对每个输入的字符串进行处理。

  2. 初始化标志变量:定义一个整型变量 flag 并将其初始化为 1,flag 作为一个标志,用于标记当前字符串是否为回文串,初始值为 1,表示假设该字符串是回文串。

  3. 读取当前字符串:依然使用 getline 函数读取一行字符串,并将其存储到变量 a 中。操作如下:

cpp
getline(cin, a);
  1. 获取字符串长度:使用 size 函数获取字符串 a 的长度,并将其赋值给变量 n。操作如下:
cpp
int n = a.size();
  1. 检查字符串是否为回文串:使用 for 循环遍历字符串的前半部分,循环变量 i 从 0 开始,到 n/2 - 1 结束。

在每次循环中,比较字符串的第 i 个字符和第 n - 1 - i 个字符是否相等,如果不相等,说明该字符串不是回文串,将 flag 置为 0,并使用 break 语句跳出循环。具体操作如下:

cpp
for (int i = 0; i < n / 2; i++)
    if (a[i] != a[n - 1 - i])
    {
        flag = 0;
        break;
    }
  1. 输出判断结果:根据 flag 的值输出判断结果,如果 flag 为 1,说明该字符串是回文串,输出 yes,否则输出 no。操作如下:
cpp
if (flag == 1)
    cout << "yes" << endl;
else
    cout << "no" << endl;

参考代码

cpp
#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;
}

基于 MIT 协议发布