10.8 string 的常用操作
vector 有的成員函式,string 幾乎都有:size、empty、push_back、pop_back、clear、at、begin、end……邏輯完全相同(size() 一樣回傳無號整數——10.4 的地雷照樣會炸)。表列之外的同名函式(string 也有 insert、erase……)參數寫法不一定跟 vector 相同,用到再查、別直接照搬。這一節補上 string 獨有、解題最常用的幾招。
¶串接:+ 與 +=
#include <iostream>
#include <string>
using namespace std;
int main() {
string a = "I", b = "AACPSCHOOL";
string c = a + " love " + b; // + 串接(和字面常數混用也行)
cout << c << '\n';
c += "!"; // += 接在尾巴
cout << c << '\n';
return 0;
}
執行結果:
I love AACPSCHOOL
I love AACPSCHOOL!
¶substr:取出子字串
子字串是從原字串裡挑出連續的一段:"cd"、"cdd" 都是 "abcdd" 的子字串,"bd" 不是(不連續)。s.substr(pos, len) 取出「從索引 pos 開始、長度 len」的子字串;len 省略就一路取到結尾:
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "abcde";
cout << s.substr(1, 3) << '\n'; // 從索引 1 開始、長度 3
cout << s.substr(2) << '\n'; // 只給起點:一路取到結尾
return 0;
}
執行結果:
bcd
cde
substr 是解題中出場率最高的 string 成員函式,值得練到反射動作。三條邊界行為一起記:pos 恰等於 s.size() 時得到空字串;pos 超過 s.size() 會直接報錯(RE——本站環境實測 "abcde" 取 substr(6) 丟出 out_of_range);len 超過剩餘長度不會錯,取到結尾為止。另外,substr 每次都複製出一份新字串,成本跟取出的長度成正比——不是免費的「切一刀」。
¶find:搜尋
s.find(t) 回傳 t 在 s 裡第一次出現的起始索引;沒找到時回傳特殊值 string::npos。所以「有沒有出現」的標準寫法是跟 npos 比(find 的回傳型態也是 10.4 那種無號整數——判斷時直接跟 npos 比,不要先存進 int 再比):
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "I love AACPSCHOOL";
cout << s.find("AA") << '\n'; // 第一次出現的索引
cout << (s.find("BB") == string::npos) << '\n'; // 真的沒找到
if (s.find("AA") != string::npos) { // 「有沒有出現」的標準寫法
cout << "found" << '\n';
}
return 0;
}
執行結果:
7
1
found
小心一個常見手滑:if (s.find(t)) 是錯的——t 出現在開頭時回傳 0,當條件用就是 false,「有找到」被誤判成「沒找到」。永遠跟 string::npos 比。另外,string 還有一群名字很像的搜尋親戚:從後往前找的 rfind、「找一群字元中任何一個」的 find_first_of 與 find_last_of——名字像、行為不同,很多人搞混,用到時先查 cppreference 確認再用。
¶比較:一個符號的字典序
8.9 苦練的字典序,string 時代一個符號收工——<、==、> 比的正是字典序:
string s = "app", t = "apple";
cout << (s < t) << '\n'; // 1:前 3 格全同,s 先結束——先結束者小
cout << (s == "app") << '\n'; // 1:相等判斷也是一個符號搞定
¶效率陷阱:迴圈裡的 ans = ans + s
成本速記(呼應 10.1 的 O 標籤):s.size()、s[i] 是 O(1);複製整串、字典序比較最壞要掃過整串(O(n));substr(pos, len) 複製出長度 len 的新字串;find 可能從頭掃到尾,不是 O(1);stoi、to_string 的成本跟位數成正比。一行函式呼叫不代表一步完成——估時間時把這些算進去。
動手試試看:解掉 ABC417 A - A Substring,把最常用的武器練到反射動作——動手前重讀上面的效率警告,累加字串記得用 +=。