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 的子串。注意,x 和 l 并非子串的左右端点。
该方法的时间复杂度为 \(O(n)\)。
例如:
string s="hijack";
string s1=s.substr(2,4);
此时 s1 应当为字符串 "jack"。
(4) clear
清空字符串。
(5) to_string(x)
将数值 x 转换为 string 类型并返回,其中 x 可以是任意数值类型,如 int、double 等。
(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 进行比较,如果 s1 与 s2 相同,则返回 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)
声明一个大小为 1000 的 bitset b。
声明一个大小为 1000 的 bitset 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。

浙公网安备 33010602011771号