string
头文件:#include <string>
ps. 头文件
string.h
和 头文件cstring
支持 c 语言中的字符串函数,但不支持 c++ 中的 string 类。
1 string 对象的构造
1.1 string str(const char* s)
将字符串初始化为 s 指向的字符串。
string str("hello world!");
2. string(size_type n, char c)
创建一个包含 n 个元素的 string 对象,其中每个元素被初始化为字符 c。
string str(10, 'a');
3. string(const string& str)
将字符串初始化为字符串 str。
string str("hello world!");
string sstr(str);
4. string str
缺省创建一个字符串对象,默认长度为 0。
string str;
5. string str(const char* s, size_type n)
将字符串初始化为 c 字符串 s 的前 n 个字符。如果 n 大于 s 的长度,超出部分可能会出现乱码。
string str("hello world", 50);
6. string str(string::iter.begin(), string::iter.end())
7. string str(const string& str, string size_type pos=0, size_type n=npos)
6.3.2 string的访问
【注】读入和输出 string 只能用 cin 和 cout,但是可以用 printf("%s",str.c_str());
1. 通过下标直接访问
printf("%c",str[1]);
2. 通过迭代器访问
string
和 vector
的迭代器,支持对迭代器直接进行 加减某个数字。
ATTENTION
string
的迭代器不需要指明类型,可以直接声明。
string::iterator it;
end()
指向的是 最后一个元素的下一个,而非最后一个元素。
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str="abcd";
for(string::iterator it=str.begin();it!=str.end();it++)
printf("%c",*it);
return 0;
}
6.3.3 string常用函数
1. operator +=
拼接两个string
。
【注】string
可以直接和 char
拼接!
str3=str1+str2;
str1+=str2;
str2=str2+'c';
str3='d'+str2;
+ 与 += 的比较
string对象的运算符+和运算符+=效率差很多,+比+=更耗时间更占空间。(因为前者是将两个string的对象相加,并赋给一个新的对象;而后者直接将等号右边的内容追加到左边的string后边。)
+ 的小tip
必须保证每个加法运算符(+)的两侧的运算对象都至少有一个是string。
例如:string str="abc"+"de";
就会出错,因为 + 会先把等号右边的计算出来,再追加到左边,而等号右边的两个字符串常量无法相加。
同理:str+=str[1]+'a';
也会出错,因为 char型变量的相加是 ASCII 码相加,所以最后追加到str
末尾的是 ASCII 码等于str[1]+'a'
的字符。
2. compare operator
两个 string
可以直接用 ==、!=、<、<=、>、>=
比较大小。默认按字典序比较。
3. length() / size()
length()
返回 string
的长度,即存放的字符数,时间复杂度为O(1)。
size()
与 length()
基本相同。
4. insert()
复杂度:O(n)
insert(pos,string)
在pos
的位置处插入字符串string
。insert(it,it2,it3)
it
为插入位置,it2
和it3
为要插入字符串的首尾迭代器,表示将串[it2,it3)
插入到it
位置上。
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str="abcxyz",str2="opq";
str.insert(str.begin()+3,str2.begin(),str2.end());
return 0;
}
5. erase()
复杂度:O(n)
- 删除单个元素 -
erase(it)
it
为需要删除的元素的迭代器。
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str="abcxyz";
str.erase(str.begin()+4);
return 0;
}
- 删除一个区间内所有的元素 -
erase(first,last)
first
和last
为区间的首尾迭代器,即删除[first,last)
。
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str="abcxyz";
str.erase(str.begin()+4,str.end());
return 0;
}
- 删除一个区间内所有的元素 -
erase(pos,length)
pos
为删除的起始地址,length
为删除的字符个数。
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string str="abcxyz";
str.erase(3,2);
return 0;
}
6. clear()
清空string
中的数据,时间复杂度为O(1)。
7. substr()
substr(pos, len)
返回从pos
位置开始,长度为len
的子串,时间复杂度为 O ( l e n ) O(len) O(len)。substr(pos)
返回从pos
位置开始到 字符串末尾 的子串,时间复杂度为 O ( s i z e ) O(size) O(size)。
8. string::npos
string::npos
是一个常数,本身值为 -1。unsigned_int
类型数据,可以认为是 unsigned_int
类型的最大值。
9. find()
- 线性搜索比较
str.find(str2)
当str2
是str
的子串时,返回其在str
中 第一次 出现的位置,否则返回string::npos
。时间复杂度 O ( s t r . s i z e ( ) ∗ s t r 2. s i z e ( ) ) O(str.size() * str2.size()) O(str.size()∗str2.size())。str.find(str2, pos)
从pos
位置开始匹配str2
,返回其在 之后第一次 出现的位置,否则返回string::npos
。时间复杂度 O ( ( s t r . s i z e ( ) − p o s ) ∗ s t r 2. s i z e ( ) ) O((str.size() - pos) * str2.size()) O((str.size()−pos)∗str2.size())。
10.replace()
时间复杂度为O(str2.length())。
str.replace(pos,len,str2)
把str
从pos
位置开始的长度为len
的字串替换成str2
。str.replace(it1,it2,str2)
把str
的迭代器[it1,it2)
范围的子串替换为str2
。
11.atoi()、stoi()、strtol()
atoi()
头文件:#include <cstdlib>
将string
类型转换为int
型。溢出不会报错。遇到非法字符会停止。只能转换为十进制数。
int a=atoi(str.c_str());
stoi()
头文件:#include <string>
将string
类型转换为int
类型。溢出会报错。遇到非法字符会停止。只能转换为十进制数。
int a=stoi(str.c_str());
strtol()
头文件:#include <cstdlib>
将stirng
类型转换为int
类型。溢出不会报错。第三个参数可以设置进制。
int a=strtol(str.c_str(),NULL,radix)
12. to_string()
将数值(int、long long、float、double)转换为字符串,返回到对应的字符串。
str=to_string(111);
13. resize()
str.resize(n)
- 将字符串长度调整为 n,若 str.size() < n,则在末尾插入默认字符扩展长度;若 str.size() > n,则截断。str.resize(n, c)
- 将字符串长度调整为 n,若 str.size() < n,则在末尾插入字符 c 扩展长度;若 str.size() > n,则截断。
6.3.4 例题
【例】A1060 Are They Equal (25 分)
ATTENTION
- 这道题实际上要做的就是把输入的字符串转换成0.xxxx*10k的形式。但是测试数据很讨厌,有些数据开头有无效零,也有0.0000的数据。
- 大体思路是分为小于1的数和大于1的数(即去掉先导零以后,第一位是数字还是小数点)处理。前者需要注意的是,有.0000的情况要特别处理。 在计算小数点的时候,因为会遍历字符串,要特别关注有没有下标越界的情况。
- 前者的幂次是负的!!!
#include <string>
#include <iostream>
using namespace std;
int n,k1,k2;
string s1,s2;
int main()
{
cin>>n>>s1>>s2;
if(s1=="0"){}
else
{
while(s1[0]=='0') s1=s1.substr(1,s1.size()-1);
if(s1[0]=='.') //<1的数 找第一个非零的数
{
int p1=0;
int num=p1+1;
while(s1[num]=='0'&&num<s1.size()) num++;
if(num==s1.size())
s1="0";
else
{
k1=-(num-p1-1);
s1=s1.substr(num,s1.size()-num); //非零位
}
}
else
{
int p1=0;
while(s1[p1]!='.'&&p1<s1.size()) p1++;
k1=p1;
if(p1!=s1.size())
s1=s1.substr(0,p1)+s1.substr(p1+1,s1.size()-p1);
}
}
if(s2=="0"){}
else
{
while(s2[0]=='0') s2=s2.substr(1,s2.size()-1);
if(s2[0]=='.') //<1的数 找第一个非零的数
{
int p2=0;
int num=p2+1;
while(s2[num]=='0') num++;
if(num==s2.size())
s2="0";
else
{
k2=-(num-p2-1);
s2=s2.substr(num,s2.size()-num); //非零位
}
}
else
{
int p2=0;
while(s2[p2]!='.'&&p2<s2.size()) p2++;
k2=p2;
if(p2!=s2.size())
s2=s2.substr(0,p2)+s2.substr(p2+1,s2.size()-p2);
}
}
if(s1.size()<n)
for(int i=0;i<n;i++)
s1=s1+'0';
if(s2.size()<n)
for(int i=0;i<n;i++)
s2=s2+'0';
if(k1!=k2)
{
cout<<"NO"<<" 0.";
for(int i=0;i<n;i++)
cout<<s1[i];
cout<<"*10^"<<k1<<" 0.";
for(int i=0;i<n;i++)
cout<<s2[i];
cout<<"*10^"<<k2;
return 0;
}
else
{
for(int i=0;i<n;i++)
if(s1[i]!=s2[i])
{
cout<<"NO"<<" 0.";
for(int i=0;i<n;i++)
cout<<s1[i];
cout<<"*10^"<<k1<<" 0.";
for(int i=0;i<n;i++)
cout<<s2[i];
cout<<"*10^"<<k2;
return 0;
}
cout<<"YES"<<" 0.";
for(int i=0;i<n;i++)
cout<<s1[i];
cout<<"*10^"<<k1;
}
}
【例】*A1100 Mars Numbers (20 分)
ATTENTION
- 这道题麻烦就麻烦在13的整数倍和0的处理。
对于13的整数倍,并不是像十进制那样,输出10/20等等,而是只输出十位数! - 所以,在自然数转火星文时,判断自然数num是否大于等于13。如果小于13,则一定是一位火星文。如果大于13,再判断num/13是否为0,如果是0,只输出十位,否则要输出十位和个位。
- 还有需要注意的是,在处理进制转换时,使用
while(num)
一定要特别处理num=0
的情况!!! - 火星文转自然数时,根据火星文的长度判断是两位数还是一位数。注意
tret
是三个字符啊啊啊!!!还要注意空格哇哇哇!!! - 由于输入可能有空格,所以不能用
cin
,用getline
哈。然后用getline
的话,记得用getchar
吞到前面那个cin
遗留下来的换行符。
cin>>str;
遇到空格、tab、回车等空白符即结束读入
getline(cin,str);
头文件:#include < string >
,可接受空白符并输出
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
const string ge[13]={"tret","jan","feb","mar","apr","may","jun","jly","aug","sep","oct","nov","dec"};
const string higher[13]={"","tam","hel","maa","huh","tou","kes","hei","elo","syy","lok","mer","jou"};
int n;
int main()
{
cin>>n;
getchar(); //
for(int i=0;i<n;i++)
{
string str;
getline(cin,str); //getline
//数字转火星文 柳神的办法更好!
if(str[0]>='0'&&str[0]<='9')
{
int num=0;
for(int j=0;j<str.size();j++)
num=num*10+(str[j]-'0');
bool flag=false;
vector<string> ans;
if(num==13)
ans.push_back(higher[1]);
else
{
if(num==0) //!!!
ans.push_back(ge[0]);
while(num)
{
int cur=num%13;
num/=13;
if(!flag)
{
if(cur!=0)
ans.push_back(ge[cur]);
flag=true;
}
else
ans.push_back(higher[cur]);
}
}
for(int j=ans.size()-1;j>=0;j--)
{
if(j!=0)
cout<<ans[j]<<" ";
else
cout<<ans[j];
}
cout<<endl;
}
else //火星文转数字
{
int res=0;
if(str.size()>4) //两位火星文
{
string s1=str.substr(0,3);
for(int j=0;j<13;j++)
{
if(higher[j]==s1)
res=j*13;
}
string s2=str.substr(4,str.size()-4);
for(int j=0;j<13;j++)
{
if(ge[j]==s2)
res+=j;
}
}
else //一位
{
for(int j=0;j<13;j++)
{
if(higher[j]==str)
res=j*13;
}
for(int j=0;j<13;j++)
{
if(ge[j]==str)
res+=j;
}
}
cout<<res<<endl;
}
}
return 0;
}
【例】*A1084 Broken Keyboard (20 分)
worn out 疲惫不堪的;耗尽的
original a.原来的,开始的
typed-out 输入
detect v.检测
captilize vt. 用大写字母写或印刷;
ATTENTION
- 大小写不敏感。
- 按检测到的顺序输出
- 其实不用存储在vector中,直接输出就行了。
- 柳神使用的
str.find()
。对于s1中的每个字符,在s2和ans中进行查找,如果他在s2中找不到,并且他的大写形式在ans中找不到的话,说明第一次遇到这个worn out键,把他加入ans中。最后输出ans即可。
#include <iostream>
#include <string>
#include <vector>
#include <set> //有序的 不行 需要无序的
using namespace std;
string s1,s2;
bool vis[100]={0}; //0-9 A-Z _
vector<char> ans;
int main()
{
cin>>s1>>s2;
for(int i=0;i<s2.size();i++)
{
if(s2[i]>='a'&&s2[i]<='z')
s2[i]=s2[i]-'a'+'A';
if(s2[i]>='0'&&s2[i]<='9')
vis[s2[i]-'0']=1;
else if(s2[i]>='A'&&s2[i]<='Z')
vis[s2[i]-'A'+20]=1;
else
vis[50]=1;
}
for(int i=0;i<s1.size();i++)
{
int idx=0;
if(s1[i]>='a'&&s1[i]<='z')
s1[i]=s1[i]-'a'+'A';
if(s1[i]>='0'&&s1[i]<='9')
idx=s1[i]-'0';
else if(s1[i]>='A'&&s1[i]<='Z')
idx=s1[i]-'A'+20;
else
idx=50;
if(vis[idx]==0)
{
ans.push_back(s1[i]);
vis[idx]=1;
}
}
for(int i=0;i<ans.size();i++)
cout<<ans[i];
return 0;
}
【例】*A1132 Cut Integer (20 分)
an even number 偶数
an odd number 奇数
ATTENTION
substr(起始位置,子串长度);
int a = stoi(str);
#include <iostream>
#include <string>
//#include <cstdio>
using namespace std;
int n;
string str;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>str;
int len=str.size()/2;
int a=stoi(str.substr(0,len));
int b=stoi(str.substr(len,len));
//printf("%d",stoi(str.substr(len,len).c_str()));
int c=stoi(str);
if(a*b!=0&&c%(a*b)==0)
cout<<"Yes\n";
else
cout<<"No\n";
}
return 0;
}
【例】*A1140 Look-and-say Sequence (20 分)
Look-and-say 看图说话
ATTENTION
string
的+=
会比+
快很多很多,空间也会省很多很多。 这道题用=
会超时。- 获取一个字符串连续数字的个数:将
pre
初始化为字符串的第一个字符,并且将cnt
初始化为0(因为第一个字符还会被比较一次,所以初始化为0)。遍历字符串,直到遇到一个不同的字符,此时前一个字符的数量就是cnt
,保存。并且更新pre
为当前字符,cnt为1,当前字符也要被计算。继续遍历即可。对于最后一串相同的字符,需要在循环结束后对齐保存。
char pre=str1[0];
int cnt=0;
for(int i=0;i<str1.size();i++)
{
if(str1[i]==pre)
cnt++;
else
{
pre=str1[i];
str2=str2+str1[i-1]+(char)(cnt+'0');
cnt=1;
}
}
str2=str2+str1[str1.size()-1]+(char)(cnt+'0');
- 记一下数字转
string
的函数:to_string(x)
。很实用。 - 这道题的题干难读.jpg
- 表示依旧不懂为什么题目里说了D不为1,样例里D就为1了orz…This definition works for D = 1 as well. …艹
#include <iostream>
#include <string>
using namespace std;
int n;
string str1,str2;
int main()
{
ios::sync_with_stdio(false);
cin>>str1>>n;
n--;
while(n--)
{
str2="";
int idx=0;
while(idx<str1.size())
{
int i;
for(i=idx+1;i<str1.size();i++)
{
if(str1[i]!=str1[idx])
break;
}
int cnt=i-idx;
//str2=str2+str1[idx]+(char)(cnt+'0');
str2+=str1[idx]+to_string(cnt);
//str2+=str1[idx];
//str2+=(char)(cnt+'0');
idx+=cnt;
}
str1=str2;
}
cout<<str1<<endl;
return 0;
}