C++【STL】【string类的使用】
目录
string类的常用接口说明
1. string类对象的常见构造
2. string类对象的容量操作
3. string类对象的访问及遍历操作
【LeetCode】反转字符串
string的迭代器
范围for
反向迭代器
常量正向/反向迭代器
4. string类对象的修改操作
find vs rfind
【牛客】字符串最后一个单词的长度
getline
5. string类非成员函数
6. string结构的说明
g++下string的结构
reserve/resize可以提前扩容好大小
7.插入擦除函数
insert
erase
【LeetCode】字符串相加
8.字符串转换相关的函数
to_string vs stoi
部分练习
【Leetcode】字符串中的第一次出现的唯一一次的字符
【LeetCode】验证回文串
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。
1. 字符串是表示字符序列的类
2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
4. 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
string - C++ Reference
打印asc码中的字符
void test12()
{for(unsigned char ch=0;ch<128;++ch){cout<<ch<<" ";}
}
int main()
{test12();return 0;
}
asc码表中的部分字符时不可以显式打印出来的
string类的常用接口说明
string内部可能就是下面的结构
_str //存储具体的字符串
_size //存储具体字符串的长度
_capacity //存储字符串的最大长度
1. string类对象的常见构造
(constructor)函数名称 | 功能说明 |
string | 构造空的string类对象,即空字符集 |
string(const char*s) | 用C-string来构造string类对象 |
string(size_t n,char c) | string类对象中包含n个字符c |
string(const string&c) | 拷贝构造函数 |
#include<iostream>
#include <string>
using namespace std;
void test()
{//构造空参的字符串string s1;//构造传参的字符串string s2("hello world");//由于隐式类型转换,将hello world转换成了一个字符串对象然后拷贝给s10。(编译器优化后可能会变成直接构造)string s10="hello world";s1=s2;s1="xxxx";//赋值s1='y';//输入字符串cin>>s1;//打印字符串cout<<s1<<" "<<s2<<endl;//拷贝构造string s3(s2);cout<<s3<<endl;//拷贝构造一部分//如果省略掉最后一个参数,就默认从pos位置拷贝到最后string s4(s2,6);cout<<s4<<endl;//从第六个位置开始拷贝两个。//如果拷贝的长度超出了字符串的最大长度,就拷贝到结尾就停止了string s5(s2,6,2);cout<<s5<<endl;//这里utf8的字符集对于中文是三个字节一个中文字符,所以一共会将前7个字符打印出来string s6("喝了这碗鸡汤,我肯定得死,你们也白想活着!",21);cout<<s6<<endl;//将x重复打印10次,但是好像不支持中文字符string s7 (10, 'x');cout<<s7<<endl;
}int main()
{test();return 0;
}
2. string类对象的容量操作
函数名称 | 功能说明 |
size | 返回字符串的有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间的总大小 |
empty | 检测字符串释是否为空串,是返回TRUE,否则返回FALSE |
clear | 清空有效字符 |
reverve | 为字符串预留空间 |
resize | 将有效字符的个数改成n个,多出的空间用字符c填充 |
注意:
1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
2. clear()只是将string中有效字符清空,不改变底层空间大小。3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
#include<iostream>
#include <list>using namespace std;
void Teststring1()
{// 注意:string类对象支持直接用cin和cout进行输入和输出string s("hello, world!!!");//size是我们真实的有效的字符长度,上面s的长度就是15cout << s.size() << endl;//length主要是为了兼容以前的C风格代码准备的cout << s.length() << endl;//这里的capacity指的值系统给我们字符串预开辟的空间大小cout << s.capacity() << endl;cout << s << endl;// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小s.clear();cout << s.size() << endl;cout << s.capacity() << endl;// 将s中有效字符个数增加到10个,多出位置用'a'进行填充// “aaaaaaaaaa”s.resize(10, 'a');cout << s.size() << endl;cout << s.capacity() << endl;// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充// "aaaaaaaaaa\0\0\0\0\0"// 注意此时s中有效字符个数已经增加到15个s.resize(15);cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中有效字符个数缩小到5个s.resize(5);//有效字符为5个的话,原先多出来的字符串就会被截断了cout << s.size() << endl;//预先开辟的容量大小是不会变得cout << s.capacity() << endl;cout << s << endl;
}int main(int argc, char *argv[])
{Teststring1();return 1;
}
void Teststring2()
{string s;// 测试reserve是否会改变string中有效元素个数s.reserve(100);cout << s.size() << endl;cout << s.capacity() << endl;// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小s.reserve(50);cout << s.size() << endl;cout << s.capacity() << endl;
}
int main()
{Teststring2();return 1;
}
从下面的测试代码中可以看出如果我们后一次开辟的空间大小比前一次小的话,其底层还是保持原先大的空间。
void Teststring2()
{string s;// 测试reserve是否会改变string中有效元素个数s.reserve(50);cout << s.size() << endl;cout << s.capacity() << endl;// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小s.reserve(100);cout << s.size() << endl;cout << s.capacity() << endl;
}
int main()
{Teststring2();return 1;
}
从下面的测试结果中可以看出,如果我们原先开辟的空间较小,然后我们又重新开辟了一块较大的空间的时候,我们的底层开辟的空间就会变大。
3. string类对象的访问及遍历操作
函数名称 |
功能说明 |
operator[] (重点) |
返回pos位置的字符,const string类对象调用 |
begin+ end |
begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend |
begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
范围for |
C++11支持更简洁的范围for的新遍历方式 |
其operator使我们的string能够像char[]一样随机提取元素,其底层的原理可能如下。
char& operator[](size_t pos)
{assert(pos<size);return _str[pos];
}
所以s1[0]='x'就等价于s1.operator[](0);
引用做返回
1、减少拷贝
2、修改返回对象
void test3()
{string s1("helloworld");for(size_t i=0;i<s1.size();i++){s1[i]++;}cout<<s1<<endl;const string s2("helloworld");for(size_t i=0;i<s2.length();i++){cout<<s2[i]<<" ";}cout<<" "<<endl;//范围越界会报错
// cout<<s2[100]<<endl;
}
int main()
{test3();return 0;
}
【LeetCode】反转字符串
力扣
给你一个字符串
s
,根据下述规则反转字符串:
- 所有非英文字母保留在原有位置。
- 所有英文字母(小写或大写)位置反转。
对于这道题在我们的c++中就可以采取我们下面的写法
class Solution {
public:string reverseOnlyLetters(string s) {size_t begin=0,end=s.size()-1;while(begin < end){while(begin<end&&!isalpha(s[begin])){++begin;}while(begin<end&&!isalpha(s[end])){--end;}swap(s[begin],s[end]);++begin;--end;}return s;}
};
string的迭代器
iterator是一个像指针一样的类型,有可能就是指针,也有可能不是指针,但是它的用法像指针一样。
字符串的迭代器
string::iterator
容器的迭代器
vector<int>::iterator
list<int>::iterator
void test4()
{string s="hello world";string::iterator it =s.begin();
//对于string和vector可以将!=改成<但是对于别的不一定
//因为string和vector存储的是连续的数据,并且用的是原生的指针,
//对于别的STL可不一定。while(it!=s.end()){cout<<*it<<" ";++it;}cout<<endl;//iterator是所有容器通用访问方式,用法类似的list<int> lt(10,1);list<int>::iterator lit=lt.begin();while(lit!=lt.end()){cout<<*lit <<" ";++lit;}cout<<endl;
}int main()
{test4();return 0;
}
但是string不喜欢用迭代器,因为[]更好用。
vector不喜欢用iterator,因为[]更好用
list/map/set……只能用迭代器访问
iterator是所有容器通用的访问方式,并且用法是类似的。
范围for
范围for相较于迭代器,是不可以反向的
void test5()
{//范围for -自动迭代,自动判断结束。//依次取s中的每个字符,赋值给chstring s="hello world";for(auto ch :s){cout<<ch<<" ";}cout<<endl;//范围for对于容器都能支持list<int> lt(10,1);for(auto e :lt){cout<<e<<" ";}cout<<endl;//范围for的底层其实就是迭代器
}
int main()
{test5();return 0;
}
反向迭代器
void test6()
{string s("hello");string::reverse_iterator rit=s.rbegin();while(rit !=s.rend()){cout<<*rit<<" ";++rit;}cout<<endl;
}
int main()
{test6();return 0;
}
常量正向/反向迭代器
常量迭代器过程中得到的数据不可修改
void PrintString(const string&str)
{
//常量正向迭代器string::const_iterator it=str.begin();
//可以使用自动推断
// auto it=str.begin();while(it!=str.end()){cout<<*it<<" ";++it;}cout<<endl;//常量反向迭代器string::const_reverse_iterator rit=str.rbegin();
//可以使用自动推断
// auto it=str.begin();while(rit!=str.rend()){cout<<*rit<<" ";++rit;}cout<<endl;
}int main()
{string s("hello");PrintString(s);return 0;
}
4. string类对象的修改操作
函数名称 |
功能说明 |
push_back |
在字符串后尾插字符c |
append |
在字符串后追加一个字符串 |
operator+= (重点) |
在字符串后追加字符串str |
c_str(重点) |
返回C格式字符串 |
find + npos(重点) |
从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind |
从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr |
在str中从pos位置开始,截取n个字符,然后将其返回 |
注意:
1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
// 测试string:
// 1. 插入(拼接)方式:push_back append operator+=
// 2. 正向和反向查找:find() + rfind()
// 3. 截取子串:substr()
// 4. 删除:erase
void Teststring5()
{string str;str.push_back(' '); // 在str后插入空格str.append("ho"); // 在str后追加一个字符"ho"str += 'm'; // 在str后追加一个字符'm'str += "mo"; // 在str后追加一个字符串"mo"
//这里使用的是流输出
//打印用的是string的重载cout << str << endl;
//这里用的是const char*的重载cout << str.c_str() << endl; // 以C语言的方式打印字符串//c_str()生成一个const char *指针,指向首元素的地址// 获取file的后缀string file("string.cpp");size_t pos = file.rfind('.');cout<<pos<<endl;string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;// npos是string里面的一个静态成员变量// static const size_t npos = -1;// 取出url中的域名string url("http://www.cplusplus.com/reference/string/string/find/");cout << url << endl;size_t start = url.find("://");//会找到://第一个字符所在的位置4cout<<start<<endl;if (start == string::npos){cout << "invalid url" << endl;return;}//跳过://的位置start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;// 删除url的协议前缀pos = url.find("://");//pos+3是将://全部删光url.erase(0, pos + 3);cout << url << endl;
}int main()
{Teststring5();return 1;
}
对于c_str()是为了兼容c语言,就如fopen中读取的字符串只能是c语言的char[]类型,所以我们下面一定要这样写才能兼容fopen
void test10()
{string filename("cplustext/main.cpp");FILE* fout=fopen(filename.c_str(),"r");assert(fout);char ch=fgetc(fout);while(ch !=EOF){cout<<ch;ch=fgetc(fout);}
}
int main()
{test10();return 1;
}
对于string 和string.c_str()进一步的测试
void test11()
{string filename("test.cpp");cout<<filename<<endl;cout<<filename.c_str()<<endl;filename+='\0';filename+="string.cpp";//\0不是可以显示的字符,所以打印不出来,会是一个奇怪的字符cout<<filename<<endl;//以string对象的size为准输出字符cout<<filename.c_str()<<endl;//常量字符串对象,遇到\0就停止打印。cout<<filename.size()<<endl;string copy=filename;cout<<copy<<endl;}
int main()
{test11();return 0;
}
void test7()
{string s("砸");//push_back只支持一个一个字符从后面插入s.push_back(' ');s.push_back('-');//append可以append一整个字符串s.append("瓦鲁多!!");cout<<s<<endl;string str1("赛扣泥");string str2("嗨忒鸭子大,哈哈");//+=的底层就是push_back和appends+='@';s+=str1;s+=str2;cout<<s<<endl;//可以直接把str1从开头到结尾全部追加到s中s.append(str1.begin(),str1.end());cout<<s<<endl;//可以指定追加的范围string copy(s.begin()+3,s.end()-3);cout<<copy<<endl;
}
int main()
{test7();return 0;
}
find vs rfind
查找函数
void test13()
{string filename("kono.dio.da");//查找后缀,默认从0位置开始查找//返回第一个匹配的位置,如果匹配不到就返回string::npos,也就是整型的最大值size_t pos=filename.find('.');if(pos!=string::npos){string suff=filename.substr(pos,filename.size()-pos);cout<<suff<<endl;}//返回最后一个匹配到的位置size_t pos2=filename.rfind('.');if(pos!=string::npos){string suff=filename.substr(pos2,filename.size()-pos);cout<<suff<<endl;}}
int main()
{test13();return 0;
}
利用上面的函数,编写一个分解url成协议,域名和后面的资源名的函数
void DealUrl(const string&url)
{size_t pos1=url.find("://");if(pos1==string::npos){cout<<"非法的url"<<endl;return;}string protocol=url.substr(0,pos1);cout<<protocol<<endl;size_t pos2=url.find('/',pos1+3);if(pos2==string::npos){cout<<"非法的url"<<endl;return;}string domin=url.substr(pos1+3,pos2-pos1-3);cout<<domin<<endl;string uri=url.substr(pos2+1);cout<<uri<<endl;
}
void test14()
{string url1="https://www.cplusplus.com/reference/string/string/?kw=string";cout<<url1<<endl;DealUrl(url1);
}
int main()
{test14();return 0;
}
【牛客】字符串最后一个单词的长度
描述
计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)
输入描述:
输入一行,代表要计算的字符串,非空,长度小于5000。
输出描述:
输出一个整数,表示输入字符串最后一个单词的长度。
字符串最后一个单词的长度_牛客题霸_牛客网
getline
如果是的cin>>str的话,在读取到空格或者回车就会停止读取,需要使用getline来读取一行数据
#include <iostream>
#include <string>
using namespace std;int main(){string str;
// cin>>str;//找到最后一个空格getline(cin,str);size_t pos=str.rfind(' ');if(pos !=string::npos){cout<<str.size()-pos-1<<endl;}else{cout<<str.size()<<endl;}
}
5. string类非成员函数
函数 |
功能说明 |
operator+ |
尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) |
输入运算符重载 |
operator<< (重点) |
输出运算符重载 |
getline (重点) |
获取一行字符串 |
relational operators (重点) |
大小比较 |
6. string结构的说明
void test8()
{string s1;string s2("111111");cout<<s1.max_size()<<endl;cout<<s2.max_size()<<endl;cout<<s1.capacity()<<endl;cout<<s2.capacity()<<endl;
}
int main()
{test8();return 0;
}
capacity是会动态变化的,但是max_size是确定的
void TestPushBack()
{string s;//这里的容量是没有计算\0的空间size_t sz = s.capacity();cout << "capacity changed: " << sz << '\n';cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}int main()
{TestPushBack();return 0;
}
可以看到在clion的编译器下,初始开辟是22,
在vs下面可能为15,
在Linux的g++下可能为1->2->4->8……
在Linux的g++下面的编译结果是这样的
g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:
空间总大小
字符串有效长度
引用计数struct _Rep_base {size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount; };
reserve/resize可以提前扩容好大小
reserve是保留 开空间
resize是开空间+初始化
void TestPushBack()
{string s;s.reserve(1000);//这里的容量是没有计算\0的空间size_t sz = s.capacity();cout << "capacity changed: " << sz << '\n';cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}int main()
{TestPushBack();return 0;
}
测试resize
void TestPushBack()
{string s;s.resize(1000);//这里的容量是没有计算\0的空间size_t sz = s.capacity();cout << "capacity changed: " << sz << '\n';cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}int main()
{TestPushBack();return 0;
}
这里为什么reverse在后面添加数据的时候并不会继续扩容,而resize会继续扩容呢?
这是因为reverse是提前开辟空间,接下来存就是从头开始存了,而resize开辟了空间,接着就是从这个空间往后存了。
在clion的环境下,当开辟的大小到1008时,就会扩容到2015了
7.插入擦除函数
insert
下面的insert插入,erase擦除或者是replace都会是要大量移动字符串中元素的,所以效率都不高
#include<iostream>
#include <list>using namespace std;
void test8()
{//在每个空格的位置插入百分号string str("ko no dio da");for(size_t i=0;i<str.size();){if(str[i]==' '){str.insert(i,"20%");//这个4是20%和下一个空格,从而达到下一个单词i+=4;}else{++i;}}cout<<str<<endl;
}int main()
{test8();return 1;
}
erase
#include<iostream>
#include <list>using namespace std;
void test8()
{//在每个空格的位置插入百分号,并且不保留空格string str("ko no dio da");for(size_t i=0;i<str.size();){if(str[i]==' '){str.erase(i,1);str.insert(i,"20%");I+=3;}else{++i;}}cout<<str<<endl;
}int main()
{test8();return 1;
}
或者采用下面用空间换时间的方式
#include<iostream>
#include <list>using namespace std;
void test8()
{//在每个空格的位置插入百分号string str("ko no dio da");for(size_t i=0;i<str.size();){if(str[i]==' '){str.erase(i,1);str.insert(i,"20%");i+=4;}else{++i;}}cout<<str<<endl;
}
void test9()
{string newstr;string str("ko no dio da");for(size_t i=0;i<str.size();++i){if(str[i]!=' '){newstr+=str[i];}else{newstr+="20%";}}cout<<newstr<<endl;
}
int main()
{test9();return 1;
}
【LeetCode】字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
来源:力扣(LeetCode)
链接:力扣
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
第一种方法,就是将两个数据从尾部相加,如果有进位,就将进位保存在一个临时的变量中,参与下一次的相加,然后将相加后的数据头插到结果的字符串中,直到两个数据都全部完成相加。
class Solution {
public:string addStrings(string num1, string num2) {
//分别获取两个字符串的数组的最大下标int end1=num1.size()-1,end2=num2.size()-1;
//用next来存储进位int next=0;
//创建存储结果的字符串string strRet;
//只要有一个字符串中还有数据就要继续循环while(end1>=0 || end2>=0){
//如果读取到的非空的话,就将字符串转换为数值int val1=end1>=0 ?num1[end1]-'0' :0;int val2=end2>=0 ?num2[end2]-'0' :0;int ret=val1+val2+next;
//进位最多也只是9+9,最大也仅仅是1next=ret>9 ? 1:0;
//可以采用insert指定在0号位置,插入一个元素,然后将我们计算过后的结果转换为字符串传入// strRet.insert(0,1,(ret%10)+‘0’);
//或者是使用迭代器,将迭代器的开始位置传入,然后将我们计算过后的结果转换为字符串传入strRet.insert(strRet.begin(),'0'+(ret%10));
//循环迭代--end1;--end2;}//处理1+9之类的情况,如果尾部为0,而十位上的读取都已经没有数据了,//但是还有进位的话就需要再头插1if(next){strRet.insert(strRet.begin(),'1');}return strRet;}};
上面头插的版本由于头插的效率比较低,需要遍历字符串我们可以看到我们的代码运行仅仅打败了7%,所以我们可以采用下面先尾插然后将整个字符串逆置的思路来提升我们的代码运行效率
class Solution {
public:string addStrings(string num1, string num2) {int end1=num1.size()-1,end2=num2.size()-1;int next=0;string strRet;while(end1>=0 || end2>=0){int val1=end1>=0 ?num1[end1]-'0' :0;int val2=end2>=0 ?num2[end2]-'0' :0;int ret=val1+val2+next;next=ret>9 ? 1:0;//尾插strRet+=('0'+ ret%10);--end1;--end2;}//如果next还有进位就需要再尾插1if(next==1){strRet+='1';}//逆置reverse(strRet.begin(),strRet.end());return strRet;}};
【Leetcode】【字符串相乘】_桜キャンドル淵的博客-CSDN博客
8.字符串转换相关的函数
to_string vs stoi
void test15()
{int ival;double dval;cin>>ival>>dval;string istr= to_string(ival);//默认保持小数点后6位string dstr= to_string(dval);cout<<istr<<endl;cout<<dstr<<endl;istr="9999";dstr="9999.99999";//string to intival=stoi(istr);dval=stoi(dstr);cout<<ival<<endl;cout<<dval<<endl;
}
int main()
{test15();return 0;
}
部分练习
【Leetcode】字符串中的第一次出现的唯一一次的字符
给定一个字符串
s
,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回-1
。力扣
class Solution {
public:int firstUniqChar(string s) {
//定义一个计数数组int a[26]={0};int len=s.size();for(int i=0;i<len;i++){a[s[i]-'a']++;}for(int i=0;i<len;i++){if(a[s[i]-'a']==1){return i;}}return -1;}
};
【LeetCode】验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
力扣
class Solution {
public:bool isPalindrome(string s) {//大写字符转小写字符for(auto& ch :s){if(ch >='A'&&ch<='Z'){ch+=32;}}int begin=0,end=s.size()-1;while(begin<end){while(begin<end&&!isalnum(s[begin]) ){++begin;}while(begin<end&&!isalnum(s[end]) ){--end;}if(s[begin]==s[end]){++begin;--end;}else{return false;}}return true;}
};
C++【STL】【string类的使用】相关推荐
- C++ 笔记(22)— STL string 类(字符串赋值、访问、拼接、查找、翻转、大小写转换)
1. 实例化和赋值 STL string #include <string> #include <iostream>int main () {using namespace s ...
- c++ string类_C++|细说STL string类概貌及底层细节
C语言中的字符串称为C风格字符串,是一个以'0'结尾的字符数组,string.h库只提供了有限.不甚安全的字符串操作函数.char str[]只能定义编译期确定大小的字符串,而保存在堆内存的动态字符数 ...
- C++ STL string类的compare函数使用
#include <iostream> #include <string> using namespace std;int main() {string a("aBc ...
- 【C++】STL —— String类不会怎么办? 看文档(万字详解)
- STL 的string类怎么啦?
STL 的string类怎么啦? 陈皓 前言 上个周末在和我的同学爬香山闲聊时,同学说到STL中的string类曾经让他备受折磨,几年前他开发一个系统前对string类还比较清楚,然后随着程序的复杂度 ...
- STL库中string类内存布局的探究
在STL中有着一个类就是string类,他的内存布局和存储机制究竟是怎么样的呢? 这就是建立好的string 可以看出,图中用黄色框框标注的部分就是主要区域 我们用来给string对象进行初始化的字符 ...
- stl之string类用法详细总结
标准c++中String类非常强大,合理使用,能极大提高编程效率,下面就对string类的用法进行总结. 头文件 #include<string> String类的构造函数如下: 1) ...
- C++STL之string类
最近在准备ccfcsp,于是学习了一下STL标准库的string类,在此记录. 作为STL类介绍的第一篇文章,首先简单介绍一下为什么要用STL,原因就是解决了一个问题之后,就不要对这个问题再来做重复的 ...
- 【STL】string详解(string类常用的操作函数、构造函数、赋值操作、子符串的拼接、查找和替换、比较、存取、插入和删除、获取)
目录 1. string容器 简介 2. string类常用的操作函数 3. 构造函数 4. 赋值操作 5. 字符串拼接 6. 字符串查找和替换 7. 字符串比较 8. 字符串存取 9. 字符串插入和 ...
最新文章
- android用户引导页,android欢迎界面引导页
- SAP MM 启用批次管理的物料MB21创建预留单据时批次号可以为空!
- 分摊的意思_会计分摊是什么意思
- 如何脚踏实地构建Java Agent
- java代理的学习,通过类实现接口来实现代理。proxy来创建动态类,和InvocationHandler接口的实现,和工作原理。...
- python中函数定义_Python中函数的定义与使用
- MySQL(8)存储过程和函数
- 95-290-240-源码-内存管理-StreamRecord-StreamRecord简介
- 【520有奖征文】 老同学聚会,20年IT行业从业感悟
- 整理:状态机的编程思想
- 大三,在软件工程学习上的感悟
- java 启动resin,Resin 启动时报错!解决方法
- 计算机b类核心期刊有哪些,B类期刊推荐有哪些
- 【密码学】七、密钥管理
- Visual Leak Detector内存泄漏检测工具,vld使用及原理
- Netty相关面试题汇总
- 1024: 计算字母序号 ZZULIOJ
- php外翻截骨术,楔形截骨术与V形截骨术治疗拇外翻效果相似
- node.js+vue.js+mysql实现登录注册的功能(前后端分离)
- Revit二次开发学习笔记