STL教程概述
也許你在解決 TopCoder 上的問題時已經用過 C++,可能你會發現別人在實現算法的時候,代碼很簡潔幹練。又或許你是一個初級 C++ 程序員,那麼你就更應該好好讀一下這篇stl教程了。在本教程中,我會逐步向你介紹 C++ STL(Standard Template Library)的強大特性,這樣你在各種 OJ 上挑戰的時候或者在工作中,在算法實現上能節省很多時間。
1. stl教程之Containers
要學好 STL,首先要從 Containers 開始。當你要處理一系列的元素的時候,必然離不開某種特定的 container,在原生的 C 語言中,只有一種 container ——數組(array)。但是 array 只有基本的功能,而我們往往需要功能很強大的 container。比如以下功能:
1)往 container 中添加 string;
2)刪除 container 中的 string;
3)查詢 container 中是否有一個 string;
4)返回 container 中不同元素的個數;
5)遍歷整個 container 獲取裏面的所有 string 列表。
當然你可以利用 array 自己來實現所有這些功能,但是這就要你花很多時間處理很多繁瑣的細節。所以還是明智點,直接用 STL 吧。
在使用 STL 時,要先 #include 適當的頭文件。例如你要用 stack,那麼就應該在程序開頭加上這句:
#include <stack>
STL 的各 Container 以及 algorithms 並定義在命令空間“std”中,並不是定義在全局的命名空間中,所以在 include 對應頭文件後,還得加上這句:
using namespace std;
還有一點要注意的是,container 的類型都是模板參數類型,模板參數放在 <>(尖括號中),如定義int類型的 vector,如下:
vector<int> v1;
在定義嵌套的 container 的時候,尖括號間要加上適當的空格:
vector< vector<int> > v1; // 正確的定義方式
vector<vector<int>> v2; // 錯誤的定義方式,編譯器會把 >> 和輸出操作的 >> 混淆
2. stl教程之Vector
STL 中最簡單的 container 是 vector,vector 只是簡單的擴展了一下 array 的功能,它也是唯一個能向後兼容 C 標準的 container,因爲它本身就是 array,只是多了一些特性而已。
vector<int> v(10);
for(int i = 0; i < 10; i++) {
v[i] = (i+1)*(i+1);
}
for(int i = 9; i > 0; i--) {
v[i] -= v[i-1];
}
vector<int> v;
在定義 v 的時候,就創建了一個空的 vector。
注意下面這行代碼:
vector<int> v[10];
在這裏,v 是一個長度爲 10 的數組,每個數組元素是一個 vector,而不是 int。但是我們的本意是要定義一個 vector,其長度爲 10,即存放 10 個整數的一個 vector。我們應該這樣做
vector<int> v(10);
要用圓括號,而不是方括號。
要知道vector的長度,我們可以調用size函數:
int elements_count = v.size();
關於size函數,我們要注意兩點:(1)size() 返回的是無符號整型,但是有時我們可能會定義一些宏,如 sz(C),這個宏用於返回 C 的長度,它返回的是 int 類型(有符號的);(2)在判斷一個 container 是否爲空的時候,最好不要用 v.size() 和 0 比較來判斷,而是用 empty() 函數:
bool is_nonempty_notgood = (v.size() >= 0); // 最好不要這這樣寫
bool is_nonempty_ok = !v.empty(); // 應該這樣寫
因爲並不是所有的 containers 的 size() 函數都是 O(1) 的,因爲有的 container 在調用 size() 函數的時候,要做遍歷操作等。
vecto r的另一個常用函數是 push_back(),該函數向 vector 尾部添加一個新的元素,並把 vector 的 size 增 1 :
vector<int> v;
for(int i = 1; i < 1000000; i *= 2) {
v.push_back(i);
}
int elements_count = v.size();
不用擔心內存分配問題 —— 在分配內存的時候,vector 並不是逐一申請每個元素的內存的,每次在進行 push_back 的時候,如果空間不夠大,它會一次性申請多個元素的空間。
如果你要調整 vector 的大小,可以調用 resize() 函數:
vector<int> v(20);
for(int i = 0; i < 20; i++) {
v[i] = i+1;
}
v.resize(25);
for(int i = 20; i < 25; i++) {
v[i] = i*2;
}
resize() 函數調整出來的 vector 會包含它原先有的那些元素,但是如果你把空間調小到不足以容納原來那些元素的程序,那麼末尾的元素會被捨棄。如果 resize() 的空間大於原先的長度,那麼新創建的元素會被填充 0 。
注意如果你是在調用 resize() 函數後調用 push_back(),系統還是會分配新的空間來存放添加進來的元素:
vector<int> v(20);
for(int i = 0; i < 20; i++) {
v[i] = i+1;
}
v.resize(25);
for(int i = 20; i < 25; i++) {
v.push_back(i*2); // Writes to elements with indices [25..30), not [20..25) !
}
如上我們把 v 調大到 25,然後調用 push_back() 函數添加一些元素,但是新的元素是從下標爲 25 開始的位置開始存放的,而不是 20 。
在清空 vector,就調用 clear() 函數,這個函數會使得 vector 包含的元素個數變成 0,而不是把元素的值變爲 0 。
初始化 vector 的方式有很多種,也可以用已有的 vector 來初始化新聲明的 vector:
vector<int> v1;
// ...
vector<int> v2 = v1;
vector<int> v3(v1);
如上初始化後, v2 和 v3 是一樣的。
如果你要創建指定開度的 vector,可以如下調用:
vector<int> Data(1000);
如上定義了一個長度爲 1000 的 vector,它所有元素的初值會被置爲 0 。如果你不想用默認的初始值,你可以自己指定:
vector<string> names(20, “Unknown”);
如上定義了一個長度爲 20 的 vector,存放 string 類型,所有元素初值爲 "Unknown" 。
還可以定義多維的 vector,最簡單的二維 vector 定義如下:
vector< vector<int> > Matrix;
我們也可以指定二維 vector 的長度:
int N, M;
// ...
vector< vector<int> > Matrix(N, vector<int>(M, -1));
如上,我們定義了一個 N*M 的矩陣,所有元素初值爲 -1 。
現在我們知道 push_back() 函數總是會在 vector 的末尾添加元素,但是如果我們想在指定的位置添加元素的話,那麼就得調用 insert() 函數了;對應的 erase() 函數用於刪掉指定的元素,我們稍後介紹完 iterators 講到它們。
要注意一點:如果你把 vector 當成參數直接傳遞給一個函數,那麼該函數在接收的時候,實際上會創建一個這個 vector 的拷貝,通常這會花費很多時間和空間,而實際上往往不需要這麼做:
void some_function(vector<int> v) { // 除非你確定要這樣做,否則最好不要這樣寫
// ...
}
我們應該這樣寫(傳遞 vector 的引用)
void some_function(const vector<int>& v) { // OK
// ...
}
加上 const 限定,被調用函數就不能修改 vector 的內容。如果你想讓被調用函數能修改原 vector 的內容,那麼應該這樣寫:
int modify_vector(vector<int>& v) { // Correct
V[0]++;
}
3. stl教程之Pairs
在講 iterators 前,我們先來簡單講一下 pairs,pairs 在 STL 中的應用非常廣泛。pairs 的基本定義如下:
template<typename T1, typename T2> struct pair {
T1 first;
T2 second;
};
pair<int, int> 定義了一對整數,pair<string, pair<int, int> > 定義一個 string 和兩個整數對,可如下訪問其元素值:
pair<string, pair<int,int> > P;
string s = P.first; // 指前面的 string
int x = P.second.first; // 指後面的第一個 int
int y = P.second.second; // 指後面的第二個 int
pairs 最大的作用是它內部有一個比較函數,可用於元素間的比較。如果兩個 pairs 的第一個元素不相等,那麼它們的大小由第一個元素的比較決定;否則由第二個元素的比較決定。元素類型爲 pairs 的 array 或 vector,可以非常方便的調用 STL 內部的排序函數來排序。
pairs 在關聯容器(associative containers)中的使用很廣泛,稍後我們會講到。
4. stl教程之Iterators
在訪問 containers 中的數據的時候,就得用到 Iterators 了。如下示例是一個反轉數組元素的程序,我們先用類 C 的方式來實現(第一種方法):
void reverse_array_simple(int *A, int N) {
int first = 0, last = N-1;
While(first < last) {
swap(A[first], A[last]); // swap(a,b) is the STL function
first++; // Move first index forward
last--; // Move last index back
}
}
我們還可以用指針來寫(第二種方法):
void reverse_array(int *A, int N) {
int *first = A, *last = A+N-1;
while(first < last) {
Swap(*first, *last);
first++;
last--;
}
}
然後我們再來考慮一個問題:如何對一個雙向鏈表進行反轉操作呢?顯然按上面的第一種方法是不可行的,因爲它基於元素下標索引。而在雙向鏈表中, 我們沒法在 O(1) 的時間內取得所有結點的下標。但是第二種方法是可行的,因爲它通過指針來操作元素:獲取元素值、比較元素大小、指針自增、指針自減,Iterators 就實現這些基本功能。所有的 STL container 都可以通過 iterator 遍歷。
Iterators 和指針很類似,它定義瞭如下基本操作:
1)獲取一個 Iterators 的值:int x = *iter;
2)自增、自減 Iterators:iter++, iter--;
3)可以用 != < 等比較運算符對不同的 Iterators 進行比較;
4)Iterators 移動動指定的偏移位置,如 iter += 20(iter 前移 20 個元素的位置);
5)獲取兩個 Iterators 間的距離:int distance = iter2 - iter1;
但是和指針不同的是,Iterators 提供了更多的功能,它不僅可以操作 container,還可以進行範圍檢查、容器分析等。
Iterators 最大的優點是實現了代碼重用:基於 Iterators 實現的算法,可以用在不同的 containers 中。
但是並不是所有的 Iterators 都提供了相同的功能,我們可以把 Iterators 大致分爲 “普通的 Iterators” 和 “隨機訪問的 Iterators”。對於普通 Iterators,我們可以用 ==、!= 等比較運算符進行比較,也可以自增、自減,但是它們不可以相減,也不可以給普通 Iterators 增加指定的值。
基於 Iterators,前面的程序可以這樣實現:
template<typename T> void reverse_array(T *first, T *last) {
if(first != last) {
while(true) {
swap(*first, *last);
first++;
if(first == last) {
break;
}
last--;
if(first == last) {
break;
}
}
}
}
這個程序和前面的程序的最大區別在於:不用 < 進行比較,而是用 == 。
通常 STL 的算法都會用兩個 Iterators,一個 begin iterator、一個 end iterator,begin 指向 container 的第一個元素,end 指向 container 最後一個元素的下一個位置。每個 STL container 都有兩個函數 begin()、end(),分別用於獲取該 container 的 begin iterator 和 end iterator。
我們可以用 c.begin()==c.end() 來判斷 container 是否爲空,用 c.end()-c.begin() 來獲取 container 的元素個數(和 c.size() 等效),當然並不是所有 container 都可以用這個相減操作。
STL 源碼裏的反轉函數是這樣的:
template<typename T> void reverse_array_stl_compliant(T *begin, T *end) {
// We should at first decrement 'end'
// But only for non-empty range
if(begin != end)
{
end--;
if(begin != end) {
while(true) {
swap(*begin, *end);
begin++;
If(begin == end) {
break;
}
end--;
if(begin == end) {
break;
}
}
}
}
}
注意這和 C++ 標準庫的 std::reverse(T begin, T end) 函數是不一樣的。
此外,因爲模板的強大作用,STL 的 algorithms 和 functions 可以接收多種類型的 iterator:
vector<int> v;
// ...
vector<int> v2(v);
vector<int> v3(v.begin(), v.end()); // v3 equals to v2
int data[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 };
vector<int> primes(data, data+(sizeof(data) / sizeof(data[0])));
最後一行代碼通過 C 的原始數組創建一個 vector。
我們還可以這樣來創建一個新的 vector:
vector<int> v;
// ...
vector<int> v2(v.begin(), v.begin() + (v.size()/2));
如上 v2 等於 v1 的前半段。
下面是 reverse() 函數的應用實例(只反轉指定區域的元素):
int data[10] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
reverse(data+2, data+6); // the range { 5, 7, 9, 11 } is now { 11, 9, 7, 5 };
每個 container 都有兩個函數 rbegin() 和 rend(),用於返回反向的 iterators,rbegin() 指向結尾、rend() 指向開頭。在需要反向遍歷 container 的時候,可用這兩個函數。
vector<int> v;
vector<int> v2(v.rbegin()+(v.size()/2), v.rend());
上面用 v 的前半段來創建 v2,v2 的內容是 v 的前半段,但是元素順序剛好相反。
在創建 iterator 對象的時候,必須指定它的類型,類型指定可以通過在 container 後加上 ::iterator、::const_iterator、::reverse_iterator、::const_reverse_iterator 等來實現,如我們可以這樣來遍歷一個 vector:
vector<int> v;
// ...
// Traverse all container, from begin() to end()
for(vector<int>::iterator it = v.begin(); it != v.end(); it++) {
*it++; // Increment the value iterator is pointing to
}
出於性能方面的考慮,建議用 != 代替 <,用 empty() 代替 size()!=0。
find() 函數可用於查找指定範圍內是否有某個元素,如果存在,那麼返回指向第一個指定元素的 iterator,如果不存在,那麼返回值等於 end interator:
vector<int> v;
for(int i = 1; i < 100; i++) {
v.push_back(i*i);
}
if(find(v.begin(), v.end(), 49) != v.end()) {
// ...
}
如果想獲取所找到的元素的下標,那麼就用相減操作:
int i = (find(v.begin(), v.end(), 49) - v.begin();
if(i < v.size()) {
// ...
}
注意用到 STL 的 algorithms 的時候,要 #include 。
下面還有些常用的方法:
int data[5] = { 1, 5, 2, 4, 3 };
vector<int> X(data, data+5);
int v1 = *max_element(X.begin(), X.end()); // 返回 vector 中的最大值
int i1 = min_element(X.begin(), X.end()) – X.begin; // 返回 vector 中最小元素的下標
int v2 = *max_element(data, data+5); // 返回 array 中最大的元素
int i3 = min_element(data, data+5) – data; // 返回 array 中最小元素的下標
我們可以定義一個宏:
#define all(c) c.begin(), c.end()
然後有下面的排序方法:
vector<int> X;
// ...
sort(X.begin(), X.end()); // 升序排序
sort(all(X)); // 升序排序, 用 #define
sort(X.rbegin(), X.rend()); // 用 iterator 進行降序排序
5. Compiling STL Programs
首先我們來看一下一個經典的錯誤:
void f(const vector<int>& v) {
for(
vector<int>::iterator it = v.begin(); // 這裏有錯,看出來了嗎
// ...
// ...
}
錯誤在於形參被 const 修飾了,但是我們獲取的 iterator 不是 const 類型,應該改成
void f(const vector<int>& v) {
int r = 0;
// Traverse the vector using const_iterator
for(vector<int>::const_iterator it = v.begin(); it != v.end(); it++) {
r += (*it)*(*it);
}
return r;
}
然後我們來簡單介紹一下一個 GNU C 標準裏特有的擴展: typeof,typeof 用於自動推導後邊 () 裏的數據類型,如:
typeof(a+b) x=(a+b)
這行代碼定義了一個變量 x,x 的類型和 a+b 的結果類型一樣,這就是這裏 typeof(a+b) 的作用。typeof 還可用於 container 的遍歷:
#define tr(container, it) \
for(typeof(container.begin()) it = container.begin(); it != container.end(); it++)
因爲 typeof 的自動類型推導功能,我們可以利用這個宏遍歷任意類型的 container,如
void f(const vector<int>& v) {
int r = 0;
tr(v, it) {
r += (*it)*(*it);
}
return r;
}
對 vector 來說,可能這個宏定義的作用並不大,但是當我們要遍歷複雜的數據類型的時候,就非常有用了,可以省掉很多代碼。
6. stl教程之 vector 中的數據操作
我們可以用 insert() 函數向指定的位置插入一個新的元素:
vector<int> v;
// ...
v.insert(1, 42); // Insert value 42 after the first
這樣,從 第二個元素(index=1)開始的所有元素都要向後移一個位置,以給新的元素騰出空間。如果我們要批量插入元素,這樣調用這個函數效率就比較低了,因爲插入一個元素,所有後面的元素都要身後移動。這裏我們可以這樣調用:
vector<int> v;
vector<int> v2;
// ..
// Shift all elements from second to last to the appropriate number of elements.
// Then copy the contents of v2 into v.
v.insert(1, all(v2));
把從第二個元素開始的所有元素都向後移動適當的位置,然後把 v2 中的所有內容填充到 v 移動元素後騰出的空間中。
同樣的,erase() 函數也有兩個類似的版本:
erase(iterator);
erase(begin iterator, end iterator);
第一個函數刪除指定位置的元素,第二個刪除指定區域的所有元素。
7. stl教程之String(字符串)
string 是 STL 中用於操作字符串的 container,它和 vector 是不一樣的。主要的區別在於它們的字符串操作和內存管理方式策略。
String 有一個 substr() 函數,這個函數只需要通過下標值操作,不依賴於 iterators,如:
string s = "hello";
string
s1 = s.substr(0, 3), // "hel"
s2 = s.substr(1, 3), // "ell"
s3 = s.substr(0, s.length()-1), "hell"
s4 = s.substr(1); // "ello"
8. stl教程之Set(集合)
STL 的 Set(集合) 有以下基本性質或操作:
1)任意兩元素都是不相等的;
2)可添加、刪除元素;
3)可獲取元素的個數;
4)可查詢集合中是否有指定元素(可在 O(logN) 時間內完成)。
先來看個簡單的例子:
set<int> s;
for(int i = 1; i <= 100; i++) {
s.insert(i); // Insert 100 elements, [1..100]
}
s.insert(42); // does nothing, 42 already exists in set
for(int i = 2; i <= 100; i += 2) {
s.erase(i); // Erase even values
}
int n = int(s.size()); // n will be 50
Set 是沒有 push_back() 函數的,因爲它的元素不需要順序,也正是這個原因,也不可通過下標來訪問集合中的元素。遍歷 Set 的唯一方法是用 iterators:
// Calculate the sum of elements in set
set<int> S;
// ...
int r = 0;
for(set<int>::const_iterator it = S.begin(); it != S.end(); it++) {
r += *it;
}
利用宏進行遍歷會更好些,想象一下如果有定義 set< pair<string, pair< int, vector > >,我們怎麼定義這玩意呢?所以還是用宏吧:
set< pair<string, pair< int, vector<int> > > SS;
int total = 0;
tr(SS, it) {
total += it->second.first;
}
注意 it->second.first,因爲 it 是個 iterator,所以我們得取到它的值,才能進行操作。這句代碼也可以這樣寫 (*it).second.first 。
要查找集合中是否指定元素,可用 find() 函數,但是注意了,STL 的全局 algorithm 裏面也有個 find() 函數,也可以用於 Set 的查找操作,但是它的複雜度是 O(N)。而 Set 本身的 find() 方法,是爲 Set 量身定做的,其算法複雜度爲 O(logN),類似的 multiset、multimap、hash_map、hash_set 中也有類似方法。
set<int> s;
// ...
if(s.find(42) != s.end()) {
// 42 presents in set
}
else {
// 42 not presents in set
}
另一個複雜度爲 O(logN) 的函數是 count() 函數:
if(s.count(42) != 0) {
// …
}
或者
if(s.count(42)) {
// …
}
但是,我個人傾向於用宏來實現和 count() 函數一樣的功能,用於 Set 中元素的 count,因爲要 count 的元素要麼存在,要麼不存在於 Set 中,沒有數量這個說法:
#define present(container, element) (container.find(element) != container.end())
#define cpresent(container, element) (find(all(container),element) != container.end())
all(c) 展開爲 c.begin(), c.end(前面我們定義過了)。
present 用於一般的 container,cpresent 用於 vector。
從集合中刪除元素,可以用 erase() 函數:
set<int> s;
// …
s.insert(54);
s.erase(29);
erase() 也可以指定區間:
set<int> s;
// ..
set<int>::iterator it1, it2;
it1 = s.find(10);
it2 = s.find(100);
// Will work if it1 and it2 are valid iterators, i.e. values 10 and 100 present in set.
s.erase(it1, it2); // Note that 10 will be deleted, but 100 will remain in the container
同樣的,其構造函數也可以用指定的區間作爲參數:
int data[5] = { 5, 1, 4, 2, 3 };
set<int> S(data, data+5);
還可以利用 Set 把 vector 中重複的元素去掉並排序:
vector<int> v;
// …
set<int> s(all(v));
vector<int> v2(all(s));
這樣,v2 會包含 v 中所有的元素,且按升序排序,且沒有重複的元素。Set 中所有可比較的元素都可以排序。
9. stl教程之Map(映射)
Map 的簡單示例如下:
map<string, int> M;
M["Top"] = 1;
M["Coder"] = 2;
M["SRM"] = 10;
int x = M["Top"] + M["Coder"];
if(M.find("SRM") != M.end()) {
M.erase(M.find("SRM")); // or even M.erase("SRM")
}
其實 Map 和 Set 很相像,只是 Map 包含的不是 values,而是 <key, value>,即鍵值對。Map 保證了 key 的唯一性,而且還支持 [] 運算符。我們可以用前面定義過的 tr() 宏輕鬆實現對 map 的遍歷。Map 的 iterator 是標準的 std::pair 類型,所以取值語句爲 it->second:
map<string, int> M;
// …
int r = 0;
tr(M, it) {
r += it->second;
}
注意在遍歷的時候,不要修改任何元素的 key,因爲這樣會破壞其實體完整性。
Map 的 map::find() 函數和 map::operator[] 有個很大的區別是:map::find() 不會改變 map 的內容,而 operator[] 會在元素不存在的時候,創建一個對應的元素。如果你不想往 map 中添加元素,那就不要用 operator[] 。如果形參是被 const 修飾的 map,那麼 operator[] 也是不可用的:
void f(const map<string, int>& M) {
if(M["the meaning"] == 42) { // Error! Cannot use [] on const map objects!
}
if(M.find("the meaning") != M.end() && M.find("the meaning")->second == 42) { // Correct
cout << "Don't Panic!" << endl;
}
}
10. Notice on Map and Set
在底層實現上,map 和 set 都是基於紅黑樹的,底層的實現細節,我們不用太過關心。我們要記住的是:map 和 set 本身就以升序的順序存儲元素,在遍歷時,map 和 set 總以升序的順序遍歷,所以說最好不要在遍歷的過程中修改 map 或 set 的 key。在算法實現上,map 和 set 是有序的這個特點非常有用。
還有重要的一點:map 和 set 的 iterators 也可用 ++ 和 -- 運算符。如下,假設 set 中有一個元素 42,且它的前驅和後續都存在,那麼下面的代碼就有效:
set<int> S;
// ...
set<int>::iterator it = S.find(42);
set<int>::iterator it1 = it, it2 = it;
it1--;
it2++;
int a = *it1, b = *it2;
這樣我們就得到 42 的左右鄰居結點了。
11. More on algorithms
STL 的 algorithms 都聲明在 #include 頭文件中,其中最基本的四個函數:
1)min(a, b),求兩者中的小者;
2)max(a, b),求兩者中的大者;
3)swap(a, b),交互兩個元素的值。
4)sort(begin, end),用於對指定區域進行升序排序,但是 sort() 函數只能對 random access iterators 進行排序,所以並不是所有的 containers 都支持 sort() 函數。注意 set 和 map 都是有序的,不需要再用 sort() 進行排序了。
還有兩個重要的函數 next_permutation(begin, end) 和 prev_permutation(begin, end),分別用於返回指定內的元素的下一個排列和上一個排列,對 next_permutation() 函數,如果已經是最後一個排列, 則返回 false;對 prev_permutation() 函數,如果已是最前面一個排列,則返回 false。但是注意,在調用 next_permutation() 函數前,要先保證 container 已經排好序了:
vector<int> v;
for(int i = 0; i < 10; i++) {
v.push_back(i);
}
do {
Solve(..., v);
} while(next_permutation(all(v));
12. stl教程之String Streams
C++ 有兩個 istringstream 和 ostringstream 用於處理 input、output 流,它們都定義在 #include
1)istringstream 用於 standard input 讀取數據:
void f(const string& s) {
// Construct an object to parse strings
istringstream is(s);
// Vector to store data
vector<int> v;
// Read integer while possible and add it to the vector
int tmp;
while(is >> tmp) {
v.push_back(tmp);
}
}
2)ostringstream 用於格式化 output:
string f(const vector<int>& v) {
// Constucvt an object to do formatted output
ostringstream os;
// Copy all elements from vector<int> to string stream as text
tr(v, it) {
os << ' ' << *it;
}
// Get string from string stream
string s = os.str();
// Remove first space character
if(!s.empty()) { // Beware of empty string here
s = s.substr(1);
}
return s;
}
13. stl教程總結
我總結了一些常用宏,可用在 TopCoder 中:
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef pair<int,int> ii;
#define sz(a) int((a).size())
#define pb push_back
#defile all(c) (c).begin(),(c).end()
#define tr(c,i) for(typeof((c).begin() i = (c).begin(); i != (c).end(); i++)
#define present(c,x) ((c).find(x) != (c).end())
#define cpresent(c,x) (find(all(c),x) != (c).end())