2025.11.12 stl 讲解

2025.11.12 stl 讲解

string

存储于 <string> 头文件,意为字符串。

1. 运算符

(1) operator =

给某一个字符串赋值。

(2) operator +

使用 + 运算符连接字符串。

string s1,s2;
s1="Nakano";
s2="Azusa";
string s3=s1+s2;

s3 的结果应为 "NakanoAzusa"

(3) operator +=

使用 += 运算符在某字符串后面追加另一字符串。

string s1,s2;
s1="Nagasaki";
s2="Soyo"
s1+=s2;

s1 的结果应为 "NagasakiSoyo"

值得注意的是,+= 运算符的时间复杂度是 \(O(n)\) 的,而 + 运算符则是 \(O(nm)\) 的。使用时,一定要注意时间复杂度的限制。

(4) operator []

通过类似数组的方式,以索引访问某字符串中的字符,下标从 0 开始。

例如声明 string s="abc",访问 s[1] 即代表字符 'b'。注意,这里返回的是 char 类型。

2. 常用方法

(1) size() 或 length()

返回字符串的长度。这两个方法是完全等效的。

注意,这两个方法返回的都是 size_t 类型(底层上就是 unsigned int 类型),使用该方法进行运算时需要注意类型的转换。

string s="internationalization";
int len=s.length();

此时 len 的值应为 20

时间复杂度为 \(O(1)\)

值得注意的是,如果你愿意用 C 风格的字符数组,想要使用 strlen 函数想要获得某字符数组的长度,那么程序的时间复杂度则在每次调用该函数的过程中都是 \(O(n)\) 的。下面是一个失败案例:

char[50] s;
cin>>(s+1);
for(int i=1;i<=strlen(s);i++){
	//TODO
}

该程序的时间复杂度是 \(O(n^2)\) 的。

正确的处理方式应是在程序一开始就把 strlen(s) 的值计算好,预储存到一个临时变量里。

所以这里不推荐用 C 风格的字符串,还是用 string 去吧。

(2) empty()

返回字符串是否为空,即是否为空串 "";而非检测某字符串是否是含有空格的字符串,例如 " " 这样的。

返回类型为 bool

(3) substr(int x,int l)

返回某字符串从位置 x 开始,一个长度为 l 的子串注意,xl 并非子串的左右端点。

该方法的时间复杂度为 \(O(n)\)

例如:

string s="hijack";
string s1=s.substr(2,4);

此时 s1 应当为字符串 "jack"

(4) clear

清空字符串。

(5) to_string(x)

将数值 x 转换为 string 类型并返回,其中 x 可以是任意数值类型,如 intdouble 等。

(6) stoi(string s) / stof(string s)

将字符串 s 转换为整型或浮点型并返回。

(7) reverse(s.begin(),s.end())

翻转字符串 s

3. 不太常用的方法

(1) find(string s)

返回某字符串中子串 s 从左到右遍历首次出现的左端点(索引从 0 开始)。如果没找到,返回 string::npos

例如:

string s="I love Azusa forever Azusa";
int pos=s.find("Azusa");

pos 应为 7,因为在字符串 s 中,子串 "Azusa" 第一次出现的左端点为索引 7

该方法使用的是暴力的匹配算法,其时间复杂度为 \(O(nm)\)

更快的字符串匹配算法 KMP,时间复杂度为 \(O(n+m)\),明显快于 find 方法。有兴趣可以研究,在此不展开。

(2) rfind(string s)

从右往左找匹配字符串 s 的位置,用法与 find 相同,只是方向变了。

(3) erase(int x,int l)

删除某字符串从位置 x 开始的,长度为 l 的子串。

删除部分的右边会自动与左边靠拢。

例如:

string s="I dislike math";
s.erase(2,3);

此时字符串 s 应为 "I like math"

时间复杂度显然为线性。

(4) replace(int x,int l,string ns)

将某字符串从位置 x开始长度为 l 的子串替换为字符串 ns

例如:

string s="I like math";
s.replace(8,4,"physics");

此时字符串 s 应为 "I like physics"

时间复杂度显然为线性。

(5) push_back(char c)

在某字符串的末尾追加字符 c

(6) at(int x)

返回字符串中指定位置 x 处的字符。

基本等效于 operator [],但它会在运行窗口中报错,不太便于调试,不推荐使用。

(7) insert(int x,string s)

在字符串 x 位置后插入字符串 s

(8) s1.compare(string s2)

将字符串 s1 与字符串 s2 进行比较,如果 s1s2 相同,则返回 0;若 s1 字典序大于 s2,则返回负值;否则,则返回正值。

(9) find_first_of(string s) 或 find_last_of(string s)

寻找目标字符串第一次出现或最后一次出现的位置,用法与 find 基本等同。

(10) find_first_not_of(string s) 或 find_last_not_of(string s)

寻找目标字符串在某字符串第一次或最后一次不匹配的位置。

4. 一些技巧

(1) 字符串 1-indexed 化

我们知道,string 类型默认是 0-indexed(以 0 为第一位置的索引)的。那么这显然不符合我们的思维习惯,所以怎么把 string 类型的第一位置索引调为 1 呢?请看以下代码。

string s = "Yamada Ryo";
s = ' ' + s;

这样就可以了。

(2) 加强 for 循环遍历字符串

使用 C++ 11 的加强 for 循环,可以快捷地遍历某字符串中的所有字符。例如:

string s = "Hakurei Reimu";
for(auto x : s){
	//TODO
}

此时 x 应为 char 类型,依次从左往右访问字符串 s 中的每个字符。

(3) 带有空格的字符串的输入

对于一个带有空格的字符串,我们怎么输入呢?显然不能直接用 cin,因为这样会使得输入流仅读取到该字符串第一个空格前的部分。我们的解决方法有两种:

cin.getline(s);

或者

getline(cin,s);

这两个函数用法是一样的,都是读取一行内所有字符组成的字符串,当换行时才停止读入。

5. 关于 basic_string

string 类型的底层是 basic_string<char>,从名称上可见,它其实就是一个动态的字符数组,也可以理解成一个 vector<char> 增加了一些强大的方法。

所以我们对于 basic_string 类,可以认为它是一个超级 vector,它不仅能支持增删改查等基本操作,还能做所有 string 能做的操作(例如 erase 这种复杂的操作)。比如可以声明 basic_string<int> 之类的东西。

虽然 basic_string 的功能很强大,但是考虑到它复杂的底层实现,它相应占用的空间也非常非常大。算法竞赛中,可能有些功能用不上,那就没必要开这种功能太多的容器了,特别容易炸空间。所以,建议按需使用 stl

6. 例题

P1470 神奇的输入方式

bitset

存储于 <bitset> 头文件。

严格上来说 bitset 不属于 stl 容器,而是一个模板类。但为了方便,我们把它归入其中考虑。

bitset 是一种能够维护固定长度的二进制数据的特殊容器。

我们考虑 C++ 内置的类型 bool,每个该类型占用的空间和 int 实际上是一样的,这导致 bool 类型的很多二进制位没有用上,造成了时间与空间的冗余。

为了使得每个二进制位都得到充分的利用,bitset 正是你的不二选择。它底层负责调控数个独立的二进制位,提供了一种高效的批量处理和操作二进制数据的方法。

1. 声明

bitset<1000>b;
bitset<500>b1(29)

声明一个大小为 1000bitset b

声明一个大小为 1000bitset b1,其初始值为 29 在二进制下的表示。

2. 运算符

(1) operator []

访问 bitset 中的某一位,返回一个布尔类型,例如:

bitset<1000>b;
//...
bool x=b[5];

表示返回 b 的第 5 位。

(2) 位运算重载符

即 operator & | ^ ~ << >> 以及 operator &= 一类的。

与基本的位运算相同,但它返回的是一个 bitset 类型。

bitset<1000>b1(5),b2(7);
bitset<1000>b3=b1^b2;

此时 b3 应为 5^7 的值,即 2

你还可以:

bitset<1000>b;
//...
b^=20;
b<<=1;

3. 常用方法

(1) count

返回 1 的个数。

例如:

bitset<1000>b(7);
int cnt=b.count();

此时 cnt 的值应为 3

(2) to_ullong 或 to_string

bitset 转换为 unsigned long long 或者 string 类型。

4. 应用

P8742 砝码称重

P5020 货币系统

P2789 直线交点数

B3611 传递闭包

对于这种布尔类型的 DP 可以使用 bitset 将时间复杂度除以一个计算机位数 w

posted @ 2025-11-11 14:51  L-Coding  阅读(7)  评论(0)    收藏  举报