目錄
- 一、string 類簡介
- 二、string 類的常用接口
- 1. 構造函數(constructor function)
- 2. 與容量相關的接口(capacity)
- 3. 與迭代器有關的接口(iterator)
- 4. 與元素訪問有關的接口(element access)
- 5. 與內容修改有關的接口(modify)
- 6. 與 string 對象操作有關的接口(operation)
- 7. string 類的關系運算符重載接口
- 8. string 類的輸入輸出
- 三、與 string 類相關的精選習題
- 1. 把字符串轉換成整數(atoi)
- 2. 字符串相加
- 3. 僅僅反轉字母
- 4. 字符串中的第一個唯一字符
- 5. 字符串最后一個單詞的長度
- 6. 驗證回文串
- 7. 反轉字符串Ⅱ
- 8. 反轉字符串中的單詞Ⅲ
- 9. 字符串相乘
一、string 類簡介
??string 是 C++ 標準庫中的一個類,該類專門用來管理字符串。相比于 C 語言中使用字符數組來管理字符串,string 類使用方便且更加強大。
??不同于 C 風格字符串,string
二、string 類的常用接口
??string 類一共有一百多個接口,而常用的接口也就二十來個。所以需要牢牢掌握這常用的二十來個結構,剩下的接口在需要的時候會查詢文檔即可。
1. 構造函數(constructor function)
??以下是 C++ 標準庫中 string 類的構造函數,上半部分是常用的構造函數,需要掌握且熟練運用;而下半部分不常用的構造函數只需了解即可。
(1)函數原型
// 構造函數// 常用的構造函數
string(); // 默認構造函數
string(const string& str); // 復制構造函數
string(const char* str) // 用 C 字符串 s 初始化
string(const char* s, size_t n); // 用 C 字符串 s 的前 n 個字符初始化
string(size_t n, char c); // 用 n 個字符 c 初始化// 不常用的構造函數
string (const string& str, size_t pos, size_t len = npos); // 用 string 類對象 str 的下標為 pos 開始的長度為 len 的子串初始化
template <class InputIterator> string (InputIterator first, InputIterator last); // 用迭代器[first, last)范圍內的字符串初始化
(2)使用演示
// string 構造函數使用演示
void test1()
{// 默認構造函數string str1;cout << "str1 = " << str1 << endl;// 使用 C 字符串初始化string str2("apple");cout << "str2 = " << str2 << endl;// 復制構造函數string str3(str2);cout << "str3 = " << str3 << endl;// 用 C 字符串的前 n 個字符初始化string str4("aaabbb", 3);cout << "str4 = " << str4 << endl;// 用 n 個字符 c 初始化string str5(5, 'e');cout << "str5 = " << str5 << endl;
}
(3)運行結果
2. 與容量相關的接口(capacity)
??與 C 字符串相比 string 類的一個明顯的優勢在于它可以根據字符串的長度進行自動增容。而 C 字符串需要提前計算好字符串的長度而開辟相應大小的字符數組。
(1)函數原型
// 常用的與容量有關的接口
size_t size() const; // 返回當前 string 類對象的長度(與其他 STL 組件保持一致)
size_t length() const; // 返回當前 string 類對象的長度(未把 string 列為 STL 之前使用的成員函數)
void resize(size_t n); // 把當前 string 對象的 size 修改至 n
void resize(size_t n, char c); // 把當前 string 對象的 size 修改至 n,并把新增的字符全部設置為 c
size_t capacity() const; // 返回當前 string 類對象的容量
void reserve(size_t n = 0); // 把當前 string 類對象的容量修改為 n
bool empty() const; // 判斷當前 string 類對象是否為空,若為空返回 true,否則返回 false// 不常用的與容量有關的接口
size_t max_size() const; // 返回 string 類對象的最大容量(可以容納的最長字符數)
void clear(); // 把當前 string 類對象的內容清空,長度變為 0,容量不變
void shrink_to_fit(); // 請求字符串減小容量來適應 size 的大小
(2)使用演示
// string 類與容量有關的函數使用演示
void test2()
{string str1("C++");// 原 size 和 capacitycout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;cout << "Length: " << str1.length() << endl;// 修改 size 并指定字符str1.resize(5, 'a');cout << "\nstr1 = " << str1 << endl;cout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;// 修改 capacitystr1.reserve(18);cout << "\nstr1 = " << str1 << endl;cout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;// 清空并判斷當前 string 對象是否為空str1.clear();cout << "\nstr1 = " << str1 << endl;cout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;if (str1.empty())cout << "Current string is empty.\n";
}
(3)運行結果
3. 與迭代器有關的接口(iterator)
??迭代器是所有 STL 容器都支持的一種訪問手段,迭代器的實質可以是指針也可以是其他自定義類型。支持迭代器也就支持范圍 for。
(1)函數原型
// 常用的迭代器接口
iterator begin(); // 返回普通 string 對象的開頭迭代器
iterator end(); // 返回普通 string 對象的尾后迭代器
const_iterator begin() const; // 返回 const string 對象的開頭迭代器
const_iterator end() const; // 返回 const string 對象的尾后迭代器
reverse_iterator rbegin(); // 返回普通 string 對象最后一個元素的迭代器
reverse_iterator rend(); // 返回普通 string 對象第一個元素前一個元素的迭代器
const_reverse_iterator rbegin() const; // 返回 const string 對象最后一個元素的迭代器
const_reverse_iterator rend(); const // 返回 const string 對象第一個元素前一個元素的迭代器// 還有幾個前綴 c 的不常用的迭代器接口,用來返回 const 類型的迭代器,也就等價于上述四個返回 const 迭代器的接口。感興趣的可以自己去查詢一下文檔
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
(2)使用演示
??對迭代器可以使用解引用操作符(*)獲得當前位置的元素。下面展示可以訪問 string 元素的幾種方法。
// string 類迭代器相關函數使用演示
// string 類迭代器相關函數使用演示
void test3()
{string str("Good night, everyone !");// 使用迭代器正序遍歷string::iterator it = str.begin();while (it != str.end()){cout << *it;++it;}cout << endl;// 使用迭代器逆序遍歷string::reverse_iterator rit = str.rbegin();while (rit != str.rend()){cout << *rit;++rit;}cout << endl;// 使用范圍 for 遍歷(編譯器在編譯階段會替換成迭代器)for (const auto& it : str)cout << it;cout << endl;// 普通 string 類對象可以使用 const 迭代器string::const_iterator cit = str.begin();// const string 類不可以使用普通迭代器const string str1("C++");//it = str1.begin(); // 報錯cit = str1.begin(); // 正確,const 對象只能使用 const 迭代器
}
(3)運行結果
4. 與元素訪問有關的接口(element access)
??string 類通過使用下標運算符訪問其類對象的元素,當然還有其他幾個不常用的接口;
(1)函數原型
// 常用的訪問元素有關的接口
char& operator[](size_t pos); // 返回普通 string 對象 pos 下標處的字符引用
const char& operator[](size_t pos) const; // 返回 const string 對象 pos 下標出的字符引用// 不常用的訪問元素有關的接口
// at 接口與上面兩個函數的區別在于,at 檢查下標時會發出異常,而下標運算符使用斷言
char& at(size_t pos);
const char& at(size_t pos) const;
// 返回當前 string 對象的最后一個字符的引用
char& back();
const char& back() const;
// 返回當前 string 對象的第一個字符的引用
char& front();
const char& front() const;
(2)使用演示
// (4)string 類訪問元素相關的接口
void test4()
{string str("little fool");// 使用下標運算符訪問 str 的每個字符元素for (int i = 0; i < str.size(); ++i)cout << str[i];cout << endl;
}
(3)運行結果
5. 與內容修改有關的接口(modify)
??string 類修改內容的接口有很多個,但最常用的是 += 操作符重載,該操作符重載既可以在末尾拼接字符串,也可以在末尾拼接單個字符。
(1)函數原型
// 常用的 string 修改內容的接口
string& operator+=(const string& str); // 在當前 string對象末尾拼接一個 string 對象 str
string& operator+=(const char* s); // 在當前 string 對象末尾拼接一個 C 風格字符串 s
string& operator+=(char c); // 在當前 string 對象末尾拼接一個字符 c
void push_back(char c); // 在當前 string 對象末尾拼接一個字符 c
string& append(const string& str); // 在當前 string 對象末尾拼接一個 string 對象
string& append(const char* s); // 在當前 string 對象末尾拼接一個 C 風格字符串
string& append(size_t n, char c); // 在當前 string 對象末尾拼接 n 個字符 c
// insert() 接口有多個重載,需要的時候需要查閱相關文檔
string& insert(size_t pos, const string& str); // 在當前 string 對象的下標 pos 處插入一個 string 對象 str
string& insert(size_t pos, const char& s); // 在當前 string 對象的下標 pos 處插入一個 C 風格字符串
string& erase(size_t pos = 0, size_t len = npos); // 刪除當前 string 對象從 pos 位置開始的長度為 len 的子串
iterator erase(iterator p); // 刪除當前迭代器位置對應的字符,返回下一個字符的迭代器
void swap(string& str); // 把當前 string 對象和 str 交換
void pop_back(); // 刪除當前 string 對象的最后一個字符// 不常用的 string 修改內容的接口
iterator erase(iterator first, iterator last); // 刪除該迭代器區間的所有字符,返回 last 后一個位置的迭代器
// assign() 接口,給當前 string 對象重新賦值,但 string 類主要使用賦值運算符重載給 string 對象重新賦值
string& assign(const string& str);
string& assign(const char* s);
// replace() 接口,替換當前 string 對象的部分內容
string& replace(size_t pos, size_t len, const string& str); // 從當前 string 對象的下標 pos 開始,使用 str 的前 len 個字符替換
(2)使用演示
// (5)string 類修改內容有關的接口
void test5()
{string str("Hello");// string 類的 += 操作符重載演示str += ' ';str += "world";string str1(" !");str += str1;cout << "str = " << str << endl;// 尾插和尾刪str.push_back('A');cout << "str = " << str << endl;str.pop_back();cout << "str = " << str << endl;// 在任意位置插入string str2("A");str2.insert(1, "apple");cout << "str2 = " << str2 << endl;int end = (int)str2.size();str2.insert(end, ".");cout << "str2 = " << str2 << endl;// 在任意位置刪除int len = (int)str2.size() - 1;str2.erase(1, len);cout << "str2 = " << str2 << endl;// 交換兩個 string 對象string str3("Java");string str4("python");cout << "str3 = " << str3 << endl;cout << "str4 = " << str4 << endl;str3.swap(str4);cout << "str3 = " << str3 << endl;cout << "str4 = " << str4 << endl;
}
(3)運行結果
6. 與 string 對象操作有關的接口(operation)
??雖然 stirng 類對象不需要以空字符結尾,但是由于 C++ 兼容 C,所以其結尾依舊保留空字符,而使用 c_str() 接口,可以獲得其 C 風格字符串的形式,也就是字符指針(char*)。
(1)函數原型
// 常用的與 string 對象操作有關的接口
const char* c_str() const; // 返回當前 string 對象首字符的地址
size_t find(const string& str, size_t pos = 0) const; // 在當前 string 對象的 pos 位置開始,尋找 stirng 對象 str 首次出現的位置
size_t find(const char* s, size_t pos = 0) const; // 在當前 string 對象 pos 位置開始,尋找 C 風格字符串 s 首次出現的位置
size_t find(char c, size_t pos = 0) const; // 在當前 string 對象 pos 位置開始,尋找字符 c
// 下面三個函數和前面三個 find 函數一致,只不過是從后往前查找
size_t rfind(const string& str, size_t pos = 0) const;
size_t rfind(const char* s, size_t pos = 0) const;
size_t rfind(char c, size_t pos = 0) const;// 不常用的與 string 對象操作相關的接口
string substr(size_t pos = 0, size_t len = npos) const; // 返回當前 string 對象 pos 位置開始的長度為 len 的子串
(2)使用演示
// (6)與 string 對象操作相關的接口
void test6()
{string str("You are very good!");// 獲取 C 風格字符串const char* s = str.c_str();cout << "str = " << str << endl;cout << "s = " << s << endl;// 在 str 中查找字符size_t pos = str.find('a');for (size_t i = pos; i < str.size(); ++i)cout << str[i];cout << endl;// 在 str 中查找字符串pos = str.find("very");for (size_t i = pos; i < str.size(); ++i)cout << str[i];cout << endl;
}
(3)運行結果
7. string 類的關系運算符重載接口
??string 類重載了所有關系運算符,可以直接使用這些關系運算符對兩個 string 對象進行比較,這些接口會按照字典順序進行比較,返回布爾值。
(1)函數原型
// string 類關系運算符重載
bool operator<(const string& str) const; // 當前 string 對象和 str 比較,若比 str 小則返回 true;否則返回 false
// 下面的接口同第一個接口
bool operator>(const string& str) const;
bool operator<=(const string& str) const;
bool operator>=(const string& str) const;
bool operator==(const string& str) const;
bool operator!=(const string& str) const;
(2)使用演示
// string 類關系運算符重載
void test7()
{string str1("C++");string str2("Java");// 開始比較cout << "str1 > str2 = " << (str1 > str2) << endl;cout << "str1 >= str2 = " << (str1 >= str2) << endl;cout << "str1 < str2 = " << (str1 < str2) << endl;cout << "str1 <= str2 = " << (str1 <= str2) << endl;cout << "str1 == str2 = " << (str1 == str2) << endl;cout << "str1 != str2 = " << (str1 != str2) << endl;
}
(3)運行結果
8. string 類的輸入輸出
??string 類的對象可以直接使用 cin 和 cout 搭配流提取運算符(>>)和流插入運算符(<<)進行輸入和輸出,因為 string 類重載了流插入和流提取運算符。string 類和 C 風格字符串在使用 cin 和流提取運算符進行輸入時也只能輸入一個單詞,遇到空白就停止輸入。
??如果需要獲取多個單詞的輸入,就需要使用 istream 類的 getline 方法來獲取一行輸入。
(1)函數原型
istream& operator>>(istream& is, string& str); // 流提取運算符重載
ostream& operator<<(ostream& os, const string& str); // 流插入運算符重載
(2)使用演示
// (8)string 類的輸入輸出
void test8()
{string str;// 輸入一個單詞cout << "Please enter a word.\n";cin >> str;cout << "The word is " << str << endl;// 注意:上一次的輸入會把換行符留在輸入流,所以需要把遺留的換行符扔掉cin.get();// 輸入一句話cout << "Please enter a sentence.\n";getline(cin, str);cout << "The sentence is " << str << endl;
}
(3)運行結果
三、與 string 類相關的精選習題
1. 把字符串轉換成整數(atoi)
題目鏈接:link
題目描述: 請你來實現一個 myAtoi(string s) 函數,使其能將字符串轉換成一個 32 位有符號整數(類似 C/C++ 中的 atoi 函數)。
函數 myAtoi(string s) 的算法如下:
- 讀入字符串并丟棄無用的前導空格
- 檢查下一個字符(假設還未到字符末尾)為正還是負號,讀取該字符(如果有)。 確定最終結果是負數還是正數。 如果兩者都不存在,則假定結果為正。
- 讀入下一個字符,直到到達下一個非數字字符或到達輸入的結尾。字符串的其余部分將被忽略。
- 將前面步驟讀入的這些數字轉換為整數(即,“123” -> 123, “0032” -> 32)。如果沒有讀入數字,則整數為 0 。必要時更改符號(從步驟 2 開始)。
- 如果整數數超過 32 位有符號整數范圍 [?231, 231 ? 1] ,需要截斷這個整數,使其保持在這個范圍內。具體來說,小于 ?231 的整數應該被固定為 ?231 ,大于 231 ? 1 的整數應該被固定為 231 ? 1 。
- 返回整數作為最終結果。
注意:
本題中的空白字符只包括空格字符 ’ ’ 。
除前導空格或數字后的其余字符串外,請勿忽略 任何其他字符。
示例:
提示:=
- 0 <= s.length <= 200
- s 由英文字母(大寫和小寫)、數字(0-9)、’ ‘、’+‘、’-’ 和 ‘.’ 組成
(1)解題思路:
- 首先使用 while 循環跳過前面的前導空格;
- 使用 if 語句檢查下一個非空格字符是否為負號,如果不是負號則為正數;
- 開始讀取數字,直到遇到第一個非數字字符。如果沒有數字字符,則為 0;
- 如果整數超過 INT_MAX 則為 INT_MAX,如果整數小于 INT_MIN 則為 INT_MIN;
- 返回結果整數。
(2)解題代碼:
class Solution {
public:int myAtoi(string str) {// 舍棄前面的前導空格int i = 0;while (' ' == str[i])++i;// 判斷正負號int flag = 1;if ('-' == str[i]){flag = -1;++i;}else if ('+' == str[i]){flag = 1;++i;}// 開始計算int len = str.size();long long result = 0;while (i < len){// 是數字字符則加入計算if (isdigit(str[i])){result = result * 10 + str[i] - '0';// 判斷計算結果if (result > INT_MAX){if (1 == flag)return INT_MAX;elsereturn INT_MIN;}}else // 否則結束計算{break;}// 下一個字符++i;}// 返回計算結果return result * flag;}
};
2. 字符串相加
題目鏈接:link
題目描述:
- 給定兩個字符串形式的非負整數 num1 和num2 ,計算它們的和并同樣以字符串形式返回。
- 你不能使用任何內建的用于處理大整數的庫(比如 BigInteger), 也不能直接將輸入的字符串轉換為整數形式。
示例:
提示:
- 1 <= num1.length, num2.length <= 104
- num1 和num2 都只包含數字 0-9
- num1 和num2 都不包含任何前導零
(1)解題思路:
- 參考數字的加法運算,從兩個字符串的最后一個字符開始相加,分別計算當前位置和進位,然后把當前位置拼接到結果字符串中,直到其中一個字符串達到末尾;
- 把剩下沒到末尾的字符串剩下的字符繼續拼接到結果字符串中,最后把整個字符串逆置;
- 返回結果字符串。
(2)解題代碼:
class Solution {
public:string addStrings(string num1, string num2) {// 所需變量string result;int i1 = num1.size() - 1, i2 = num2.size() - 1;int carry = 0;// 開始拼接while (i1 >= 0 || i2 >= 0){// 計算當前兩位int n1 = (i1 >= 0? num1[i1--] - '0' : 0);int n2 = (i2 >= 0? num2[i2--] - '0' : 0);// 拼接result += (n1 + n2 + carry) % 10 + '0';// 計算進位carry = (n1 + n2 + carry) / 10;}// 如果兩個字符串同時結束,但是 carry 不為 0if (carry)result += carry + '0';// 逆置結果字符串reverse(result.begin(), result.end());// 返回結果字符串return result;}
};
3. 僅僅反轉字母
題目鏈接:link
題目描述: 給你一個字符串 s ,根據下述規則反轉字符串:
- 所有非英文字母保留在原有位置。
- 所有英文字母(小寫或大寫)位置反轉。
- 返回反轉后的 s 。
示例:
提示:
4. 1 <= s.length <= 100
5. s 僅由 ASCII 值在范圍 [33, 122] 的字符組成
6. s 不含 ‘"’ 或 ‘\’
(1)解題思路:
- 使用兩個指針,分別指向字符串的首尾,當兩個指針未相遇并且都指向字母字符時交換,當兩個指針相遇或者錯開時則結束;
- 返回交換完畢的字符串;
(2)解題代碼:
class Solution {
public:string reverseOnlyLetters(string s) {int left = 0, right = s.size() - 1;// 開始交換while (left < right){// 左右尋找字母字符while (left < right && !isalpha(s[left]))++left;while (left < right && !isalpha(s[right]))--right;// 交換swap(s[left], s[right]);// 下一組++left;--right;}// 返回交換完成的字符串return s;}
};
4. 字符串中的第一個唯一字符
題目鏈接:link
題目描述: 給定一個字符串 s ,找到 它的第一個不重復的字符,并返回它的索引 。如果不存在,則返回 -1 。
示例:
提示:
- 1 <= s.length <= 105
- s 只包含小寫字母
(1)解題思路:
- 由于字符串只有小寫字母,那么創建一個包含 26 個元素的 int 數組,對應 26 個小寫字母的出現次數;
- 遍歷一遍數組,計算每個字符出現的次數,再遍歷一遍數組,找出第一個出現一次的字符;
- 返回對應的下標;
(2)解題代碼:
class Solution {
public:int firstUniqChar(string s) {int times[26] = {0};// 計算每個字符出現的次數for (const auto& e : s){++times[e - 'a'];}// 找到第一個出現一次的字符int len = s.size();for (int i = 0; i < len; ++i){if (1 == times[s[i] - 'a'])return i;}// 沒有找到返回 -1return -1;}
};
5. 字符串最后一個單詞的長度
題目鏈接:link
題目描述: 對于給定的若干個單詞組成的句子,每個單詞均由大小寫字母混合構成,單詞間使用單個空格分隔。輸出最后一個單詞的長度。
輸入描述:
在一行上輸入若干個字符串,每個字符串代表一個單詞,組成給定的句子。
除此之外,保證每個單詞非空,由大小寫字母混合構成,且總字符長度不超過 103。
輸出描述:
在一行上輸出一個整數,代表最后一個單詞的長度。
(1)解題思路:
- 創建一個 string 對象接受輸入,每次輸入一個單子并計算其長度,直到遇到換行符;
- 返回此時的長度,也就是最后一個單詞的長度。
(2)解題代碼:
// 頭文件
#include <iostream>
#include <string>// using 聲明
using std::string;
using std::cin;
using std::cout;
using std::endl;int main() {int len = 0;string str;// 輸入單詞并計算長度while (cin >> str){len = str.size();}// 輸出最后一個單詞的長度cout << len << endl;
}
// 64 位輸出請用 printf("%lld")
6. 驗證回文串
題目鏈接:link
題目描述: 如果在將所有大寫字符轉換為小寫字符、并移除所有非字母數字字符之后,短語正著讀和反著讀都一樣。則可以認為該短語是一個 回文串 。
字母和數字都屬于字母數字字符。
給你一個字符串 s,如果它是 回文串 ,返回 true ;否則,返回 false 。
示例:
提示:
- 1 <= s.length <= 2 * 105
- s 僅由可打印的 ASCII 字符組成
(1)解題思路:
- 首先遍歷一遍字符串,把所有大寫字母轉換成小寫字母,然后把所有字母數字字符拼接到一個新的字符串后頭;
- 然后使用兩個指針正著和倒著遍歷來驗證新的字符串是否為回文字符串;
- 返回驗證的結果;
(2)解題代碼:
class Solution {
public:bool isPalindrome(string s) {string tmp;// 小寫轉大寫并把所有的字母數字字符拼接到新的字符串后面for (int i = 0; i < s.size(); ++i){if (isalnum(s[i])){tmp += tolower(s[i]);}}// 驗證新的字符串int left = 0, right = tmp.size() - 1;while (left < right){// 判斷當前這組字符if (tmp[left] != tmp[right]){return false;}// 下一組字符++left;--right;}// 是回文串return true;}
};
7. 反轉字符串Ⅱ
題目鏈接:link
題目描述: 給定一個字符串 s 和一個整數 k,從字符串開頭算起,每計數至 2k 個字符,就反轉這 2k 字符中的前 k 個字符。
如果剩余字符少于 k 個,則將剩余字符全部反轉。
如果剩余字符小于 2k 但大于或等于 k 個,則反轉前 k 個字符,其余字符保持原樣。
示例:
提示:
- 1 <= s.length <= 104
- s 僅由小寫英文組成
- 1 <= k <= 104
(1)解題思路:
- while 循環,前后指針法,cur 指針往前走,pre 指針不動,當 cur 指針與 pre 指針的距離達到 2k-1 時,逆置前 k 個字符并更新 pre 指針的位置;
- 當 cur >= s.size() 時跳出循環,并根據此時 cur - pre 的值判斷最后一組的情況;
- 返回逆置完成的字符串。
(2)解題代碼:
// 逆置函數
void reverse(char* left, char* right)
{while (left < right){char tmp = *left;*left = *right;*right = tmp;// 下一組++left;--right;}
}class Solution {
public:string reverseStr(string s, int k) {// 使用前后指針遍歷字符串,每當兩個指針相隔 2k 時,反轉int pre = 0, cur = 0;int len = s.size();while (cur < len){if (cur - pre == 2 * k - 1){// 反轉前 k 個字符reverse(&s[pre], &s[pre + k - 1]);// 更新 prepre = cur + 1;}// cur 前進++cur;}// 判斷最后的一段字符if (cur - pre < k)reverse(&s[pre], &s[cur - 1]);elsereverse(&s[pre], &s[pre + k - 1]);// 返回逆置完畢的字符串return s;}
};
8. 反轉字符串中的單詞Ⅲ
題目鏈接:link
題目描述: 給定一個字符串 s ,你需要反轉字符串中每個單詞的字符順序,同時仍保留空格和單詞的初始順序。
示例:
提示:
- 1 <= s.length <= 5 * 104
- s 包含可打印的 ASCII 字符。
- s 不包含任何開頭或結尾空格。
- s 里 至少 有一個詞。
- s 中的所有單詞都用一個空格隔開。
(1)解題思路:
- while 循環,前后指針法,pre 不動,cur 前進,當 cur 遇到空格時,逆置前面的單詞后更新 pre,直到 cur 超出 s 末尾;
- 返回逆置完畢的 s。
(2)解題代碼:
// 逆置函數
void reverse(char* left, char* right)
{while (left < right){char tmp = *left;*left = *right;*right = tmp;// 下一組++left;--right;}
}class Solution {
public:string reverseWords(string s) {int pre = 0, cur = 0, len = s.size();while (cur < len){// 如果 cur 是空格,則逆置前面的單詞if (' ' == s[cur]){reverse(&s[pre], &s[cur-1]);// 更新 prepre = cur + 1;}// cur 前進++cur;}// 逆置最后一個單詞reverse(&s[pre], &s[cur-1]);// 返回逆置的字符串return s;}
};
9. 字符串相乘
題目鏈接:link
題目描述: 給定兩個以字符串形式表示的非負整數 num1 和 num2,返回 num1 和 num2 的乘積,它們的乘積也表示為字符串形式。
示例:
提示:
- 1 <= num1.length, num2.length <= 200
- num1 和 num2 只能由數字組成。
- num1 和 num2 都不包含任何前導零,除了數字0本身。
(1)解題思路:
- 用其中一個字符串的每一位去乘以另一個字符串,然后把結果存在一個臨時字符串中,最后加到結果字符串中去;
- 需要對不同的位進行補 0,個位不補,十位補一個 0,以此類推;
- 字符串的加法前面的習題中已經寫過了,本題只是在其基礎上進行了一些復雜的變化,難度沒有什么提升;
- 返回最終的結果字符串。
(2)解題代碼:
??本題思路沒有問題,但是代碼寫的一般般,大家可以參考自己修改完善一下。
// 字符串相加函數
string add_string(const string& num1, const string& num2)
{// 所需變量int i1 = num1.size() - 1;int i2 = num2.size() - 1;int carry = 0;string result;// 開始相加while (i1 >= 0 || i2 >= 0){// 計算兩個數字字符串當前位的值int n1 = (i1 >= 0 ? num1[i1] - '0' : 0);int n2 = (i2 >= 0 ? num2[i2] - '0' : 0);// 計算當前位int cur = n1 + n2 + carry;// 拼接當前位result += cur % 10 + '0';// 計算進位carry = cur / 10;// 下一組位--i1;--i2;} // 判斷是否存在進位if (carry)result += carry + '0';// 逆置reverse(result.begin(), result.end());// 返回結果return result;
}class Solution {
public:string multiply(string num1, string num2) {// 若一個數為 0,則結果為 0if ("0" == num1 || "0" == num2)return "0";// 所需變量int i = num1.size() - 1;string result;// 用 num1 的每一位去乘以 num2while (i >= 0){char cur = num1[i];string cur_result;int carry = 0;// 當前位開始相乘for (int j = num2.size() - 1; j >= 0; --j){// 相乘int mul = (cur - '0') * (num2[j] - '0') + carry;// 計算本位并拼接到當前結果cur_result += mul % 10 + '0';// 計算進位carry = mul / 10;}// 補上進位if (carry)cur_result += carry + '0';// 逆置當前結果reverse(cur_result.begin(), cur_result.end());// 補零int n = num1.size() - i - 1;while (n--)cur_result += '0';// 把當前結果加到最終結果result = add_string(result, cur_result);// 下一位--i;}// 返回相乘的結果return result;}
};