这篇文章将会总结一些处理字符串、进制转换等等的常见的、非常有用的技巧和函数。很多技巧的下面可能还附着相应的例题链接或者位置以供更好的理解。后续会随时更新本文章,希望大家收藏、留言,一起学习进步!
对于特别简单的函数,就不写函数的详细原型啦!
目录
char*函数
strlen函数:
求一个char数组的长度,<cstring>
char str[10]
scanf("%s",str);
int len=strlen(str);
cout<<len<<endl;
strcmp函数:
比较两个char数组的字典序,<cstring>
char a[6]="a"
char b[6]="ab"
char c[6]="ac"
cout<<strcmp(a,b)<<endl;//-1
cout<<strcmp(c,b)<<endl;//1
cout<<strcmp(a,a)<<endl;//0
strcat函数:
连接两个char数组,<cstring>
char a[6]="ab"
char b[6]="bc"
strcat(a,b)
cout<<a<<endl;//abbc
cout<<b<<endl;//bc
strcpy函数:
把一个char数组复制到另一个上面,<cstring>
char a[6]="ab"
char b[6]="bc"
strcpy(a,b)
cout<<a<<endl;//bc
cout<<b<<endl;//bc
memset函数:
初始化数组,<cstring> int数组也可以用
char a[100];
memset(a,'0',sizeof a);
cin.getline函数:
读取一行字符,包括空格,遇到换行符终止,丢弃换行符,<istream>
第一个参数:数组名,第二个参数:最多输入的字符数(注意字符串后面有一个空字符,所以一般这个参数比实际最大长度多1,比如读取abcd就写5)
int a[100];
cin.getline(a,100);//最多输入99个字符,即(0,99);
cout<<a;
还有cin.get函数可以读取一行,但是对于读取一行的函数就掌握getline就够用了!比如下面这段代码换成get就会出问题
while(n--)
{
char a[100];
cin.getline(a,100)//最多输入99个字符,即(0,99);
cout<<a<<endl;
}
读取char*时的下标问题
如果想让读取到的char*下标从1开始,cin,scanf,getline都支持下面这种写法,string就不支持
char a[10];
// cin>>a+1;
// scanf("%s",a+1);
cin.getline(a+1,10);
cout<<a[1];
return 0;
char*转string
方法非常简单,直接让一个char*赋值给string即可
char a[100]="anc";
string b=a;
cout<<b<<endl;//anc
string函数
包含STL神器之一:string 的各种常用函数,头文件皆为<string>
size/length函数
返回string长度
string a="123";
cout<<a.size()<<endl;//3
cout<<a.length()<<endl;//3
push_back()与insert()函数
前者:向字符串尾端插入一个字符
后者:下面展示常用的,当然还有别的方法,只不过可以由最基本的转换而来
string a="123";
string b="56";
char c='7';
a.push_back('4');
cout<<a<<endl;//1234
a.insert(a.begin(),b);//不合法
a.insert(0,b);//合法,a="123456"
a.insert(a.begin(),c);//合法,a="1234567"
compare函数/append函数
建议直接用< > == 和 +=符号替代
erase函数
常用来删除一个区间内的字符,区间左闭右开
string str="123456";
str.erase(1,4);
cout<<str<<end;//156
replace函数
常用来把str的某一个区间替换成重复的单个字符或者整个其它字符串
下面展示几种常用的写法
string a="123456";
string b="789";
char c='0'
a.replace(0,5,b);//a="7896" 把a的[0,5)变成b,第一个参数是起点,第二个参数是替换长度!!
a.replace(0,2,3,c);//a="00096" 把a从下标0开始往后的2个字符替换成3个字符c
string转char*
直接用c_str()即可
char a[100];
string b="123";
a=b.c_str();
//也可以这样:scanf("%s",b.c_str());
find函数
模式匹配函数,直接放例子,通俗易懂
string a="123123";
cout<<a.find('1')<<endl;//0
cout<<a.find('1',2)<<endl;//3 因为是从下标2往后找
substr函数
str.substr(pos,length)从pos开始往后截取长度为length的子串并返回
string a="1234";
string b=a.substr(2,2);//b="34"
数符转换和进制转换
本部分将会介绍整型和字符串常用的转换方法,由于这个转换常常可以伴随进制转换,所以放在一起总结,这也是字符串模拟题目中常常考察的技能!!!
sprintf函数
把一个格式化字符串,写入字符数组,返回写入数组的总字符数,如果失败,返回负数
举例:
char a[100];
int b=5;
sprintf(a,"1234%d",b);//a="12345"
itoa/atoi
itoa用于将十进制整数变为任意进制的字符数组形式
atoi用于将字符数组变为十进制整数(不支持类型转换)
详见:atoi itoa简介
进制转换补充
看到这里,可能会问:那如何把a进制整数转为b进制整数呢?答案是c++没有这个库函数,所以需要自己写一个函数,先把a进制转10进制,再把10进制转b进制
同理,c++也没有把一个a进制字符串变为十进制整数的函数,需要麻烦自己写了
字符串函数优化
find函数虽然好用,但是时间上不如kmp算法,所以当写一个题目用findTLE的使用,换成kmp吧,下面是acwing算法基础课的kmp板子:
#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
int n, m;
int ne[N];
char s[M], p[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
for (int i = 2, j = 0; i <= n; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
for (int i = 1, j = 0; i <= m; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == n)
{
printf("%d ", i - n);
j = ne[j];
}
}
return 0;
}
再来说substr,比如下面这种题(来自acwing算法基础课),第一反应可能是用substr,但可能会TLE
所以在此放一下acwing算法基础课字符串哈希的板子,大家触类旁通,在某种情况下可以参考
#include<iostream>
using namespace std;
const int N=100010,P=131;
typedef unsigned long long ULL;
char str[N];
ULL p[N],h[N];
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
int n,m;
cin>>n>>m;
getchar();
for(int i=1;i<=n;i++)scanf("%c",&str[i]);
p[0]=1;
for(int i=1;i<=n;i++)
{
h[i]=h[i-1]*P+str[i];
p[i]=p[i-1]*P;
}
while(m--)
{
int l1,r1,l2,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(get(l1,r1)==get(l2,r2))cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
位运算技巧
对于二进制字符串模拟这种题,比如csp第30次认证第3题,很多时候用到位运算的技巧。下面是链接
x&-x:可以求x最低位的一个1出现在哪一位
比如100011算完就是000001
10010算完就是00010
a>>b&1:检查a的第b位(最低位是0)是否为1
a&1==1:奇数
a&1==0:偶数
二进制“拼接”:
比如两个二进制数100和010想变成100010,只需让100左移3然后加上010即可
二进制“截取”:
比如10101想求中间的三位数“010”,首先令a=10101 a>>=1 b=1<<3 c=a-b c即为答案
~i:判断i是否为0
由此可以发现如果可以灵活使用位运算,很多时候处理二进制数的时候可以迎刃而解
字符串按空格分割
像第三次ccfcsp的第三题:csp3次认证第3题就会常常用到这种分割技巧,方法在之前的文章中已经讲过,给出连接如下:
技巧补充(持续更新)
1.类似截取引号内的字符串方法:(2023.9.25更新)
比如输入12423423“23232”,可以得到23232
其它比如截取[]内的字符串,方法同理,类比即可得到
char c;
string value;
while (c = getchar(), c != '\"');
while (c = getchar(), c != '\"') value += c;
2.连续的几行是一个处理单元(2023.11.13更新)
字符模拟题目中,输入数据是多样例,每个样例是连续的几行(行中可能有空格),如果用空行分隔开,那么就进入了下一个样例,在这种题目中可以使用如下的代码:
vector<string>strs;
while(getline(cin,str))strs.push_back(str);
for(int i=0;i<str.size();i++)
{
if(strs[i].empty())continue;
int j=i+1;
while(j<strs.size()&&strs[j].size())j++;
work(i,j-1);//对这些连续行的具体处理
i=j-1;
}
用到上面技巧的典型题目是第10次ccfcsp第三题Markdown
3.把输入的多行数据拼成一个string,从而避免对各种各样的空行、空格分类讨论(2023.11.14更新)
while(n--)
{
getline(cin,s);
str+=s;
}
典型:第11次ccfcsp第三题json查询,题解链接如下:
4.使用while循环去过滤某些字符的方法,而不是仅仅依靠string中的substr+find+replace函数去做,不仅费时间,而且代码难写(2023.11.14更新)
比如:
while (m -- )
{
getline(cin, s);
vector<string> qs;
for (int i = 0; i < s.size(); i ++ )
{
int j = i + 1;
while (j < s.size() && s[j] != '.') j ++ ;
qs.push_back(s.substr(i, j - i));
i = j;
}
query(obj, qs);
}
来源: