語法書 / AA 競程語法書 下冊 / 第十單元 / string 的常用操作

10.8 string 的常用操作

vector 有的成員函式,string 幾乎都有:sizeemptypush_backpop_backclearatbeginend……邏輯完全相同(size() 一樣回傳無號整數——10.4 的地雷照樣會炸)。表列之外的同名函式(string 也有 inserterase……)參數寫法不一定跟 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) 回傳 ts第一次出現的起始索引;沒找到時回傳特殊值 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_offind_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.1O 標籤):s.size()s[i]O(1);複製整串、字典序比較最壞要掃過整串(O(n));substr(pos, len) 複製出長度 len 的新字串;find 可能從頭掃到尾,不是 O(1)stoito_string 的成本跟位數成正比。一行函式呼叫不代表一步完成——估時間時把這些算進去。

動手試試看:解掉 ABC417 A - A Substring,把最常用的武器練到反射動作——動手前重讀上面的效率警告,累加字串記得用 +=