目錄
第一類題目:反轉(zhuǎn)字符串類型?
1.反轉(zhuǎn)字母(初級(jí))
正向迭代器 ?
題目講解 ?
?2.反轉(zhuǎn)字母(中級(jí))
reverse和size
題目講解 ?
3.反轉(zhuǎn)字母(高級(jí))?
find和resize ?
題目講解 ?
第二類題目:一個(gè)字符串的子字符串
1.模擬實(shí)現(xiàn)strStr()
KMP算法
理論講解 ?
代碼實(shí)現(xiàn) ?
使用next數(shù)組模擬實(shí)現(xiàn)strStr()
2.重復(fù)的子字符串
erase 和 operator+ ?
題目講解 ?
第三類題目:一些值得注意的細(xì)節(jié)
字符串最后一個(gè)單詞的長(zhǎng)度
getline 和 cin ?
題目講解 ?
?
????????本篇文章其實(shí)是以筆者在LeetCode的刷題經(jīng)歷為基礎(chǔ)而誕生的。string的接口有100多個(gè),實(shí)在有些繁雜,哪些應(yīng)該熟練掌握,哪些應(yīng)該簡(jiǎn)單了解,自己摸索其實(shí)成本其實(shí)還是挺高的,所以我想這一篇文章能夠帶給你幫助。內(nèi)容有些長(zhǎng),只要你耐心看完一定會(huì)有很大的收貨。各位剛接觸string的同道中人也可以先刷刷這些題試試水。? ? ? ??
第一類題目:反轉(zhuǎn)字符串類型?
反轉(zhuǎn)字母(初級(jí))
反轉(zhuǎn)字母(初級(jí))
正向迭代器 ?
在此之前,我們先來(lái)介紹關(guān)于正向迭代器的兩個(gè)接口:
??先來(lái)看如何使用:?
void test_string1()
{
string s = "abcdefg";
/*迭代器的使用:iterator 是一種在string類域中被重新命名的類型,所以string::iterator
表示類型*/
string::iterator it_begin = s.begin();
string::iterator it_end = s.end();
/*string迭代器行為類似于指針,begin()接口獲得字符串的起始位置
end()接口獲得字符串最終的位置(也就是'\0'的位置)*/
cout << *it_begin << ' ' << *(it_end - 1) << endl;
//我們可以像指針一樣操作string迭代器:
while (it_begin <= it_end - 1)
{
(*it_begin) -= 32; //全部轉(zhuǎn)成大寫字母
cout << *it_begin << ' ';
++it_begin;
}
cout << endl;
} //運(yùn)行結(jié)果如下圖:
題目講解 ?
?
這道題思路很簡(jiǎn)單,使用前后指針,往中間移動(dòng)的過(guò)程中,將字母交換即可,這道題就來(lái)使用一下迭代器,直接附上代碼實(shí)現(xiàn):
class Solution {
public:
string reverseOnlyLetters(string& s) {
//定義前后指針
string::iterator it_begin = s.begin(), it_end = s.end() - 1;
while(it_begin < it_end)
{
/*這里如果忘記怎么用isalpha函數(shù)判斷是否是字母,也可以用*it_begin > 'A'
&& *it_begin < 'Z'...... 的邏輯*/
while(it_begin < it_end && !isalpha(*it_begin))
{
++it_begin;
}
while(it_begin < it_end && !isalpha(*it_end))
{
--it_end;
}
if(it_begin < it_end)
{
swap(*it_begin, *it_end);
++it_begin;
--it_end;
}
}
return s;
}
};
?2.反轉(zhuǎn)字母(中級(jí))
反轉(zhuǎn)字母(中級(jí))
在此之前我們來(lái)介紹兩個(gè)接口: ?
reverse和size
先來(lái)看如何使用:
void test_string2()
{
//reverse和size的使用
string s = "hello";
//size獲得字符串的長(zhǎng)度,且不包含'\0'
size_t sz = s.size();
cout << sz << endl;
string::iterator it_begin = s.begin(), it_end = s.end();
/*reverse在std的命名空間中,也就是說(shuō)使用std命名空間后可以直接調(diào)用該接口
該函數(shù)的兩個(gè)參數(shù)的類型必須是迭代器,且區(qū)間是左閉右開(kāi)
該函數(shù)效果是:實(shí)現(xiàn)字符串的翻轉(zhuǎn),與上一題類似*/
reverse(it_begin, it_end);
cout << s << endl;
}//效果如下圖所示:
題目講解 ?
思路上也不算難,只不過(guò)比第一題多加了一些限制條件而已,我們以每2k個(gè)為一組進(jìn)行遍歷,將前k個(gè)進(jìn)行翻轉(zhuǎn);對(duì)于最后不滿2k個(gè)進(jìn)行特殊判斷即可,附上代碼實(shí)現(xiàn):
class Solution {
public:
string reverseStr(string& s, int k) {
for(size_t i = 0; i < s.size(); i += 2 * k)
{
//i + k <= s.size()確保迭代器不會(huì)越界
if(i + k <= s.size())
{
//對(duì)前k個(gè)進(jìn)行翻轉(zhuǎn)
reverse(s.begin() + i, s.begin() + i + k);
}
else
{
//剩余的不滿k個(gè)全部翻轉(zhuǎn)
reverse(s.begin() + i, s.end());
}
}
return s;
}
};
3.反轉(zhuǎn)字母(高級(jí))?
3??反轉(zhuǎn)字母(高級(jí))
在此之前我們先來(lái)介紹兩個(gè)接口:
find和resize ?
先來(lái)看看如何使用:
void test_string3()
{
//find的介紹:
string s = "hello world hello programmer";
//1.常見(jiàn)用法:直接傳入你想找的某個(gè)字符,返回第一個(gè)出現(xiàn)該字符的下標(biāo)(類型:size_t)
//比如這里我想找第一個(gè)空格出現(xiàn)的位置:
size_t pos1 = s.find(' ');
cout << pos1 << endl;
//2.當(dāng)然如果沒(méi)有找到,語(yǔ)法規(guī)定返回string::npos,這里來(lái)驗(yàn)證一下:
//我們找一個(gè)字符'z',但是字符串中并不存在
size_t pos2 = s.find('z');
if (pos2 == string::npos)
cout << "yes" << endl;
else
cout << "no" << endl;
/*3.小應(yīng)用:查找文件后綴名
現(xiàn)在有一個(gè)文件名為:test.cpp,我想獲得后綴如何操作?*/
string file = "test.cpp";
size_t pos3 = file.find('.');
//這里介紹一個(gè)接口:substr,給定范圍后可以返回子子字符串
string suffixName = file.substr(pos3, file.size()-pos3);
cout << suffixName << endl;
//4.另一個(gè)常見(jiàn)用法:查找模式串是否存在,比如:
string haystack = "hello world hello everyone";
string needle = "everyone";
if (haystack.find(needle) != string::npos)
cout << "find!" << '\n';
else
cout << "none!" << '\n';
/*當(dāng)然你也可以這樣用:haystack.find("everyone");
這里的原理就是:這個(gè)傳入的字符串會(huì)隱式類型轉(zhuǎn)化為string類型,然后再進(jìn)行查找
當(dāng)find只傳入需要查找的對(duì)象作為參數(shù)時(shí),默認(rèn)從0位置開(kāi)始查找,如果想從特定某個(gè)位置
開(kāi)始查找,需要傳入具體的開(kāi)始位置,例如:*/
haystack.find(needle, 5);
}//運(yùn)行結(jié)果如下圖所示:
void test_string4()
{
//resize的使用:
string s1 = " hello world ";
//使用快慢指針?lè)ǎ? size_t fast = 0, slow = 0;
while (fast < s1.size())
{
if(s1[fast] != ' ')
{
if (slow != 0)
{
s1[slow] = ' ';
++slow;
}
while (fast < s1.size() && s1[fast] != ' ')
{
s1[slow++] = s1[fast++];
}
}
++fast;
}
//resize:重新改變s1的空間大小,傳入的參數(shù)(類型size_t)是你想讓空間最終變成你指定的大小
s1.resize(slow);
cout << s1 << endl;
}//運(yùn)行結(jié)果如下圖所示:
題目講解 ?
對(duì)整個(gè)字符串進(jìn)行翻轉(zhuǎn)的操作不難: ?
//只需使用一次reverse即可:
string s = "the sky is blue";
reverse(s.begin(), s.end());
接下來(lái)如何對(duì)每個(gè)單詞進(jìn)行翻轉(zhuǎn)?
筆者一開(kāi)始是想使用find來(lái)找到每個(gè)空格的位置,以此來(lái)控制每?jī)蓚€(gè)空格之間的單詞進(jìn)行翻轉(zhuǎn),這種操作有點(diǎn)冗雜,還需要特別處理一些頭尾單詞,得到如下代碼:
string reverseSeriesWords(string& s)
{
size_t pos = s.find(' '); //找到第一個(gè)空格位置
size_t index = 0;
reverse(s.begin() + index, s.begin() + pos); //單獨(dú)控制第一個(gè)單詞
while (s.find(' ', pos + 1) != string::npos)//進(jìn)入循環(huán)進(jìn)行遍歷,每?jī)蓚€(gè)空格使用一次 reverse
{
index = pos;
pos = s.find(' ', index + 1);
reverse(s.begin() + index + 1, s.begin() + pos);
}
reverse(s.begin() + pos + 1, s.end()); //最后單獨(dú)控制最后一個(gè)單詞
return s;
}
后來(lái),經(jīng)過(guò)反思與借鑒得到更好的代碼如下: ?
void reverse(string& s, int start, int end)
{ //翻轉(zhuǎn),區(qū)間寫法:左閉又閉 []
for (int i = start, j = end; i < j; i++, j--)
{
swap(s[i], s[j]); //調(diào)用庫(kù)函數(shù)swap
}
}
void reverseSeriesWords(string& s)
{
int start = 0;
for (int i = 0; i <= s.size(); ++i)
{
if (i == s.size() || s[i] == ' ')
{ //到達(dá)空格或者串尾,說(shuō)明一個(gè)單詞結(jié)束,進(jìn)行翻轉(zhuǎn)。
reverse(s, start, i - 1); //翻轉(zhuǎn),注意是左閉右閉 []的翻轉(zhuǎn)。
start = i + 1; //更新下一個(gè)單詞的開(kāi)始下標(biāo)start
}
}
}
最終得到完整的代碼如下: ?
class Solution {
public:
void reverse(string& s, int start, int end)
{ //翻轉(zhuǎn),區(qū)間寫法:左閉又閉 []
for (int i = start, j = end; i < j; i++, j--)
{
swap(s[i], s[j]);
}
}
void removeSpaces(string& s) //移除空格我們?cè)趯size接口的時(shí)候已經(jīng)實(shí)現(xiàn)過(guò)了
{
size_t fast = 0, slow = 0;
while (fast < s1.size())
{
if(s1[fast] != ' ')
{
if (slow != 0)
{
s1[slow] = ' ';
++slow;
}
while (fast < s1.size() && s1[fast] != ' ')
{
s1[slow++] = s1[fast++];
}
}
++fast;
}
//resize:根據(jù)你的指定,重新改變s1的空間大小
s1.resize(slow);
}
string reverseSeriesWords(string& s)
{
removeSpaces(s); //去除多余空格
reverse(s, 0, s.size() - 1);
int start = 0;
for (int i = 0; i <= s.size(); ++i)
{
if (i == s.size() || s[i] == ' ')
{ //到達(dá)空格或者串尾,說(shuō)明一個(gè)單詞結(jié)束,進(jìn)行翻轉(zhuǎn)。
reverse(s, start, i - 1); //翻轉(zhuǎn),注意是左閉右閉 []的翻轉(zhuǎn)。
start = i + 1; //更新下一個(gè)單詞的開(kāi)始下標(biāo)start
}
}
return s;
}
};
第二類題目:一個(gè)字符串的子字符串
1.模擬實(shí)現(xiàn)strStr()
模擬實(shí)現(xiàn)strStr()
做這道題之前,先想一個(gè)問(wèn)題:
給定文本串:a a b a a b a a f (長(zhǎng)度假設(shè)為N)
給定模式串:a a b a a f (長(zhǎng)度假設(shè)為M)
如何在前文本符串中查找模式串是否存在?
我第一個(gè)想到的是暴力求解,直接走兩層循環(huán)來(lái)匹配,但是時(shí)間復(fù)雜度很高:O(M * N),那么有沒(méi)有更優(yōu)化得分方法?這里介紹KMP算法 ?
KMP算法
理論講解 ?
?KMP主要應(yīng)用在字符串匹配上,KMP的主要思想是當(dāng)出現(xiàn)字符串不匹配時(shí),可以知道一部分之前已經(jīng)匹配的文本內(nèi)容,可以利用這些信息避免從頭再去做匹配了。
代碼實(shí)現(xiàn) ?
而前綴表就是存放在next數(shù)組里面,下面我們介紹如何通過(guò)代碼實(shí)現(xiàn)——傳入一個(gè)模式串能夠得到其next數(shù)組的函數(shù)
1.首先我們對(duì)next數(shù)組進(jìn)行初始化
size_t i, j = 0; //定義兩個(gè)指針,之后用來(lái)遍歷
next[0] = j //將next數(shù)組第一個(gè)位置初始化為0
2.處理前后綴相同和不同的情況
首先得知道一個(gè)點(diǎn):
//給出代碼:
void getNext(int* next, const string& s) {
int j = -1;
next[0] = j;
for(int i = 1; i < s.size(); i++) //注意i從1開(kāi)始
{
//當(dāng)前后綴不同:
while (j >= 0 && s[i] != s[j + 1])
{
j = next[j]; // 向前回退
}
//當(dāng)前后綴相同:
if (s[i] == s[j + 1])
{
j++;
}
next[i] = j; // 將j(前綴的長(zhǎng)度)賦給next[i]
}
}//運(yùn)行結(jié)果如下:
至此我們next數(shù)組構(gòu)建的函數(shù)已經(jīng)完成 ?
使用next數(shù)組模擬實(shí)現(xiàn)strStr()
思路講解:首先我們先得到needle的next數(shù)組 ?
int next[needle.size()];
getNext(next, needle);
接著我們開(kāi)始遍歷haystack與needle ?
size_t j = 0;
for(size_t i = 0; i < haystack.size(); ++i)
{
//出現(xiàn)沖突時(shí)回到next數(shù)組對(duì)應(yīng)下標(biāo)處
while(j > 0 && haystack[i] != needle[j])
{
j = next[j - 1];
}
//沒(méi)出現(xiàn)沖突時(shí)一直往后匹配即可
if(haystack[i] == needle[j])
{
++j;
}
//當(dāng)j遍歷到最后等于模式串的長(zhǎng)度時(shí)仍沒(méi)有出現(xiàn)沖突即證明存在子串
//這時(shí)我們返回下標(biāo)即可
if(j == needle.size())
{
return (i - needle.size() + 1);
}
}
如下附上完整代碼:
class Solution {
public:
void getNext(int* next, string& s)
{
//初始化
size_t j = 0;
next[0] = j;
//得到next數(shù)組
for(size_t i = 1; i < s.size(); ++i)
{
while(j > 0)
{
if(s[i] != s[j])
{
j = next[j - 1];
}
else
break;
}
if(s[i] == s[j])
++j;
next[i] = j;
}
}
int strStr(string haystack, string needle) {
if(needle.size() == 0)
{
return 0;
}
int next[needle.size()];
getNext(next, needle);
size_t j = 0;
for(size_t i = 0; i < haystack.size(); ++i)
{
while(j > 0 && haystack[i] != needle[j])
{
j = next[j - 1];
}
if(haystack[i] == needle[j])
{
++j;
}
if(j == needle.size())
{
return (i - needle.size() + 1);
}
}
return -1;
}
};
2.重復(fù)的子字符串
重復(fù)的子字符串
再次之間我們先來(lái)介紹兩個(gè)接口:
erase 和 operator+ ?
void test_string7()
{
//operator+ 用法:
string s = "hello ";
string tmp = "world";
//創(chuàng)建一個(gè)新變量來(lái)接收
//可以+ 一個(gè)string對(duì)象:
string newstr = s + tmp;
cout << newstr << endl;
//也可以單獨(dú)加一個(gè)字符:
string newstr2 = newstr + '!';
cout << newstr2 << '\n';
//operator+= 用法:
s += tmp;
s += '!';
cout << s << endl;
//不過(guò)不建議多用erase接口,尾部刪除還好,如果中間刪除還要挪動(dòng)數(shù)組,時(shí)間復(fù)雜度就為O(N)了
}//運(yùn)行結(jié)果如下圖:
void test_string8()
{
//erase的用法:
string s = "hello world";
//1.刪除指定位置字符,參數(shù)為迭代器:
s.erase(s.begin());
cout << s << endl;
//2.刪除指定范圍字符串,參數(shù)為迭代器,區(qū)間為左閉右開(kāi):
string s2 = "hello world";
s2.erase(s2.begin(), s2.begin() + 5);
cout << s2 << endl;
//3.刪除指定范圍字符,參數(shù)為size_t,區(qū)間為左閉右開(kāi):
string s3 = "hello world";
s3.erase(0, 6);
cout << s3 << endl;
}//代碼演示如下圖:
題目講解 ?
這里介紹兩種解題方法:
1.移動(dòng)匹配:
接下來(lái)附上代碼: ?
class Solution {
public:
bool repeatedSubstringPattern(string s) {
string newstr = s + s;
string::iterator it_begin = newstr.begin(), it_end = newstr.end();
newstr.erase(it_end - 1); //刪除尾
newstr.erase(it_begin); //刪除頭
if(newstr.find(s) != string::npos)
{
return true;
}
else
return false;
}
};
2.KMP算法邏輯解決 ?
接下來(lái)我們以字符串"abababab"為例,得到next數(shù)組 ?
接下來(lái)附上代碼實(shí)現(xiàn): ?
class Solution {
public:
void getNext(int* next, string& s)
{
size_t j = 0;
next[0] = j;
for(size_t i = 1; i < s.size(); ++i)
{
while(j > 0 && s[i] != s[j])
{
j = next[j - 1];
}
if(s[i] == s[j])
++j;
next[i] = j;
}
}
bool repeatedSubstringPattern (string& s) {
if (s.size() == 0) {
return false;
}
int next[s.size()];
getNext(next, s);
int len = s.size();
if (next[len - 1] != 0 && len % (len - (next[len - 1] )) == 0) {
return true;
}
return false;
}
};
第三類題目:一些值得注意的細(xì)節(jié)
字符串最后一個(gè)單詞的長(zhǎng)度
字符串最后一個(gè)單詞的長(zhǎng)度
在這之前先來(lái)介紹兩個(gè)接口: ?
getline 和 cin ?
void test_string9()
{
string s1, s2;
//現(xiàn)在想對(duì)s1輸入"hello"
cin >> s1;
cout << s1 << endl;
//如果現(xiàn)在想對(duì)s2輸入"hello world"呢?
cin >> s2;
cout << s2 << endl;
} //代碼運(yùn)行結(jié)果如下圖所示:
為什么輸入了“hello world”最后打印卻只有“hello”?
因?yàn)閷?duì)于cin來(lái)說(shuō),空格就表示對(duì)當(dāng)前對(duì)象輸入完成了,而在空格后面繼續(xù)輸入,數(shù)據(jù)保存在緩存中,但是沒(méi)有另外的對(duì)象接收,在程序運(yùn)行完就自動(dòng)丟棄了。所以cin無(wú)法對(duì)一個(gè)對(duì)象輸入帶著空格的英文詞組,或者句子。
這個(gè)時(shí)候只能用getline了:
題目講解 ?
這題本身沒(méi)有什么難度,只是想?yún)^(qū)別一些getline和cin的用法,避免混淆,這里直接附上代碼:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line;
while(getline(cin, line))
{
size_t pos = line.rfind(' '); //rfind和find使用方式一樣,只不過(guò)是find是從前往后找, refind是從后往前找
cout<<line.size()-pos-1<<endl;
}
return 0;
}
好了,文章到這里結(jié)束了,本篇文章介紹了常見(jiàn)的string接口的使用,但是并不完全,實(shí)際上還需要讀者在平時(shí)的時(shí)候查查文檔,加深印象。
最后附上一個(gè)大佬對(duì)于string類的認(rèn)識(shí):
STL_string類到底怎么啦? ????????文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-816281.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-816281.html
到了這里,關(guān)于【C++】string的接口從生澀到靈活使用——這篇文章就夠了的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!