chapter4-字符串

记录字符串常考的4种操作:遍历、加密、统计、匹配

1.字符串介绍

C++提供了字符串(string)这种基本数据类型,它可以很方便地对字符串进行各种操作。使用需要添加头文件#include<string>

1.1字符串的构造

字符串的构造包括定义和初始化两个部分,定义一个字符串的方式和定义其他基本数据类型的方式相同,如string str;,此外还有很多种常用的定义方式,可根据题面需要进行选择。

字符串定义
//常见的定义字符串的一些方法 2024-02-17
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string s0 = "Initial String"; //初始化可以直接给字符串变量赋值
string s1;
string s2 (s0); //把s0的拷贝给s2
string s3 (s0, 8, 3); //从字符串s0的下标8开始取3个字符拷贝给s3(下标从0开始) Str
string s4 ("A character sequence"); //用构造函数的方式给s4值
string s5 ("Another character sequence", 12); //Another char 只需要前面12个字符
string s6 (10, 'x'); //假如你现在要建立一个字符串,这个字符串由n个重复的字符构成,使用这种方式 10个x
cout << "s0:" << s0 <<endl;
cout << "s1:" << s1 <<endl;
cout << "s2:" << s2 <<endl;
cout << "s3:" << s3 <<endl;
cout << "s4:" << s4 <<endl;
cout << "s5:" << s5 <<endl;
cout << "s6:" << s6 <<endl;
return 0;
}

1.2字符串的操作

1、对于操作来说,最重要的就是访问,要访问到字符串的每一位字符。字符串类型已经重载了[],让我们能够像访问数组元素一样用下标访问字符串的每一位。

2、第二种操作是插入,可以在字符串的任意位置插入一段字符串或者插入字符。调用str4.insert(pos1,str, pos2, n)函数,其中str4是我们定义的变量名字,有4个参数,第一个参数指明从字符串str4的哪个下标位置开始插入;第二个参数指明插入的字符串内容,可以是字符串变量str2,也可以直接显示给出;第三个参数指明从str2的哪个下标位置开始取字符;第四个参数配合前一个参数指明取字符的个数

3、移除操作。只有两种方式,1:从字符串的某个下标位置开始截断,后面都不要了。2:只删一小部分,第一个参数还是指明从哪个开始删;第二个参数指明一共删除几个字符。str4.erase(pos, n);

4、清空操作。直接调用str4.clear(),清空字符串。

字符串的操作
//字符串的元素操作 2024-02-17
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str = "hello world";
for(int i = 0; i < str.size(); ++i) {
cout << str[i];
}
cout << endl << "the 7th element of str is: " << str[6] << endl;
string str1 = "to be question";
string str2 = "that is a ";
string str3 = "or not world";
string str4;
str4.insert(0, str1); //在str4下标为0的位置插入str1 //to be question
str4.insert(6, str3, 0, 7); //在str4下标为6的位置插入,只插入str3的一部分,从0的位置开始,插7个字符 //to be (or not )question
str4.insert(13, "to be "); //也可以显示的、把要插入的字符串插入进来//to be or not (to be )question
str4.insert(19, str2); //to be or not to be (that is a )question
cout << "str4: " << str4 << endl;
str4.erase(18); //从下标哪个位置开始删除
str4.erase(0, 9);
cout << "str4: " << str4 << endl;
str4.clear();
cout << "str4: " << str4 << endl;
return 0;
}

1.3字符串的运算

字符串的运算与数组不同,字符串的加法变成了字符串的拼接;而> < = != >= <=这一类的运算符也被重载,用字典排序进行大小比较(原本字符串是没有大小之分的)。

字符串的运算符
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str1 = "to be ";
string str2 = "nor not to be ";
string str3 = "that is a question";
string str = str1 + ",";
str = str + str2 + ";";
str += str3;
cout << str << endl;
str1 = "ab";
str2 = "abc";
str3 = "bcc";
cout << (str1 <= str2) << endl;
cout << (str2 != str3) << endl;
cout << (str3 < str1) << endl;
return 0;
}

1.4字符串的常用函数

1、返回字符串长度的函数:str1.size()

2、在字符串里查找一小段字符串或者字符:str1.find("xxx"),会返回第一个等于这个小字符串的位置下标。或者可以指定从哪个位置开始找,默认从字符串的首部开始找。如果找得到就返回下标,找不到就返回-1

3、返回字符串的子串的函数:str1.substr(pos, n);有两种方式,1:给定一个下标,从这个下标开始后面所有的字符一并返回给你;2:只要某一小段,给定参数1从哪里开始,参数2总共截取几个字符。

字符串的运算符
//字符串的常用函数 2024-02-17
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str1 = "bella, Things will just be fine";
cout << str1.size() << endl; //字符个数
int position1 = str1.find("be");
int position2 = str1.find("be", 2);
cout << "position1: " << position1 << endl;
cout << "position2: " << position2 << endl;
//也可以找单个字符
position1 = str1.find('l');
position2 = str1.find('l', 5);
cout << "position1: " << position1 << endl;
cout << "position2: " << position2 << endl;
string str2 = str1.substr(7); //从下标为7的位置开始取子串
cout << "str2: " << str2 << endl;
string str3 = str1.substr(7,6); //Things
cout << "str3: " << str3 << endl;
return 0;
}

2.字符串操作

2.1 字符串-遍历

在一些题目中,要求对给定的数据进行一系列的处理,但直接处理原数据比较困难,这时如果将对原数据的处理,转化为对字符串的遍历,就会异常简单。
写个算法,对2个小于1000000000的输入,求结果。 特殊乘法举例:123 * 45 = 1 * 4 +1 * 5 +2 * 4 +2 * 5 +3 * 4+3 * 5

特殊乘法
//字符串的遍历-特殊乘法 2024-02-20
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str1, str2;
while(cin >> str1 >> str2) {
int number = 0;
for(int i=0; i<str1.size(); ++i) {
for(int j=0; j<str2.size(); ++j) {
number += (str1[i] - '0') * (str2[j] - '0');
}
}
cout << number << endl;
}
return 0;
}

2.2 字符串-加密

常见的机试题目通常是通过对字母表的循环平移来把明文加密为密文;或者通过相同手法的逆运算,从密文解出明文。
字符串加密.jpg

简单密码
//字符串的加密-简单密码 2024-02-20
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str; //多组输入,特定跳出
while(getline(cin, str)) {
if(str == "ENDOFINPUT")
break;
getline(cin, str);
for(int i = 0; i< str.size(); ++i) {
if(str[i] >= 'A' && str[i] <= 'Z') {
str[i] = (str[i] -'A' -5 + 26) %26 + 'A';
}
}
cout << str << endl;
getline(cin, str); //读掉END
}
return 0;
}

2.3 字符串-统计

给定一个字符串,统计其中一些字符出现的个数。

统计字符
//字符串的统计-统计字符 2024-02-20
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int MAXN = 128; //字符范围
int arr[MAXN];
int main()
{
string str1, str2;
while(getline(cin, str1)) {
if(str1 == "#")
break;
getline(cin, str2);
memset(arr, 0, sizeof(arr));
for(int i = 0; i < str2.size(); ++i) {
arr[str2[i]] ++;
}
for(int i = 0 ; i<str1.size(); ++i) {
printf("%c %d\n", str1[i], arr[str1[i]]);
}
}
return 0;
}

直接开一个字符范围(0~255)的辅助数组,用于记录给定字符串中每个字符出现的次数,然后输出题目指定的字符的出现次数,这样的时间复杂度仅为O(n+m),比起遍历两层字符串的时间复杂度O(mn)要好很多。

2.4 字符串-匹配

判断模式串Parrten在文本串Text中是否出现。字符串匹配由简到难一共分为4个等级:

1、Detection 是否出现

2、Location 出现位置

3、Count 出现几次

4、Enumeration 每次出现位置

其中,最关键的问题是2、Location,文本串中找到模式串出现的位置,这个问题解决,那么其他3个问题就能很自然地求解出来。

2.4.1 KMP算法

KMP算法的主体思想是:在发生失配的时候,如何快速地将模式串向后移动,以达到快速匹配的效果。

每次的跳跃距离d应该尽可能地小,不至于遗漏重要的信息,等价于模式串的真前缀和真后缀应该尽可能地长。至此,我们就能够知道每次应该跳跃多少距离。那么我们就可以用一个next表来记录跳跃的距离d。

KMP算法.jpg
(跳跃距离d仅和模式串真前缀、真后缀的相同值的最大值有关,和文本串无关,因此仅凭模式串就可以进行预处理,得到next表。)

此外,有没有可能出现模式串的真前缀和真后缀字符串都是空集的情况呢?一旦发生这种情况,我们跳跃的距离就变成了0,就会使得模式串无法继续向后移动,从而导致这个算法无法继续进行下去。

对此,KMP算法对这个问题采取的措施是将next表的第0项设为-1,作为假想的通配符。
next表构造.jpg

KMP算法
//字符串的匹配-KMP 2024-02-20
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
const int MAXN = 100+10; //Pattern length
int nextTable[MAXN];
//next表构造
void GetNextTable(string pattern) {
int m = pattern.size();
int j = 0;
nextTable[j] = -1;
int t = nextTable[j];
while(j < m) { //注意t必定<=j < m
if(t == -1 || pattern[j] == pattern[t]) { //匹配成功
t++;
j++;
nextTable[j] = t;
} else { //失败,用next表的前面这一项去尝试匹配
t = nextTable[t];
}
}
}
//KMP主体,返回模式串在文本串中第一次出现的位置下标
int KMP(string text, string pattern)
{
GetNextTable(pattern);
int n = text.size();
int m = pattern.size();
int i = 0;
int j = 0;
while(i < n && j < m) {
if(j == -1 || text[i] == pattern[j]) { //匹配成功
i++;
j++;
}else {
j = nextTable[j];
}
}
if(j == m) {
return i-j; //确实文本串中有模式串
} else {
return -1;
}
}
int main()
{
string text, pattern;
getline(cin, text);
getline(cin, pattern);
int position = KMP(text, pattern);
cout << position << endl;
return 0;
}
posted @   paopaotangzu  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示