信息学竞赛基础之字符串
此博客原文地址:https://www.cnblogs.com/BobHuang/p/12740540.html
学习了三种基本类型int(整型),double(浮点型)及char(字符型),整型可以帮我们存储整数,浮点型可以帮我们存储实数(小数),char类型可以帮助我们存储26个英文字母和阿拉伯数字等等。
但是如果我遇到了一个比较大的数字呢,比如 TZOJ1420: 手机短号 我们可以尝试用字符串这一类型解答。
一、字符串引入
字符串是以"开头以"结束中间为任意字符的串,如 "Hello world!" 、 "AAAAB" 、 " 0"、"12QWERT"、"\n",我们输出的时候已经用到多次。
字符串是字符数组,字符数组不同于其他的数组只是类型不一样。同样的字符数组也是从下标为0开始的,而不是1。我们可以这样定义数组:
[存储类型] 数组名[常量表达式1]...
字符数组也是这样:
//字符数组ch1,有ch[0]、ch[1]、ch[2]三个元素
char ch1[3];
//字符数组ch2,有16个元素。ch[0]和ch[1]均有8个元素
char ch2[2][8];
我们要如何进行赋值呢?我们有3种方法。
(1)用字符初始化数组,例如:
//定义一个ch1数组,并依次赋值
char ch1[5]={'a','b','c','d','e'};
初始值表中的每个数据项是一个字符,用字符给数组ch1的各个元素初始化。当初始
值个数少于元素个数时,从首元素开始赋值,剩余元素默认为空字符。当然如果你只是定义了这个字符数组,它和其他数组也是一样,元素的值是不定的,是垃圾。
字符数组中也可以存放若干个字符,也可以来存放字符串。两者的区别是字符串有
结束符('\0'),所以上面的ch不是字符串。
字符串是一维数组,但是一维字符数组不一定是字符串。
比如我们可以定义这样的字符串,例如:
//定义ch2数组,存放了字符串"abcd"
char ch2[5]={'a','b','c','d','\0'};
//定义ch3数组,同样存放了字符串"abcd"
char ch3[5]={'a','b','c','d'};
(2)用字符串初始化数组,例如:
//使用字符串初始化ch1数组
char ch1[5]="abcd";
使用这个格式进行字符串初始化,需要注意字符串的长度应小于字符数组的大小,因为最后一个字符存储的是'\0',我们定义字符数组需要+1。同理二维数组可存放若干个字符串,可使用若干个字符串给二维数组初始化。
//在数组ch2中存放3个字符串,每个字符串的长度不得大于3
char ch2[3][4]={"abc","012","+_="};
(3)数组元素赋值,字符数组的赋值是给该数组的各个元素赋字符值。例如:
//定义字符数组ch1,分别进行赋值
char ch1[3];
ch1[0]='a';ch1[1]='b';ch1[2]='c';
对多维数组也是如此,当需要一个数组的全部元素值赋给另一数组时,不能直接用数组名=数组名这种直接赋值的方式,要使用字符串拷贝函数来完成。
char ch1[10]="12a11";
char ch2[10];
//将ch1拷贝给ch2,不能用ch2=ch1
strcpy(ch2,ch1);
字符和字符串的区别
1⃣️两者的定界符不同,字符是‘(单引号)括起来,字符串由"(双引号)括起来。
2⃣️字符只能是单个字符,但是字符串可以是多个字符。
3⃣️可以把一个字符赋值给另一个字符,但是字符串不能直接赋值。
4⃣️字符占1B,字符串占用(l+1)B,l为字符串长度,增加的1B存放字符串结束标志'\0'。字符'0'占内存空间1B,字符串"0"占2B。
三、字符串的输入与输出
字符串可以作为一维数组来处理,一个一个输入,一个一个进行输出。
#include <bits/stdc++.h>
using namespace std;
int main()
{
char s[100];
//可以输入一个小于100的数字
int n;
while (cin >> n)
{
//循环进行输入
for (int i = 0; i < n; i++)
cin >> s[i];
s[n]='\0';
//循环进行输出
for (int i = 0; i < n; i++)
cout << s[i];
cout << '\n';
}
return 0;
}
我们也可以对字符串作为一个整体进行输入和输出。
输入
从键盘读入一个字符数组可以直接用cin语句,和其他类型一样。
//定义字符数组s,可以存储100个元素
char s[100];
//输入字符串s
cin >> s;
s是首地址,也就是&s[0],代表会从s[0]开始存储。读入完会在最后添加'\0'标志,因此我们输入字符串即可,不需要自己加'\0'。
但是空格和换行并不能存储在里面,比如我们输入了"Alice is smart",但是只能输入"Alice",因为cin读入是以空格和换行作为分割的,我们需要使用getline。getline是这样使用的,里面需要添加参数。
char ch[100];
//使用cin里面的getline,需要给首地址和长度参数,给20最多接受19
cin.getline(ch,20);
//默认已换行结束,我可以让他以'/'作为结束
//这个里面可以存储有换行
cin.getline(ch,100,'/');
如果使用C语言就是如下所示:
//读取字符串到ch里,以空格或者换行结束
scanf("%s",ch);
//读取一行字符串到ch里,以换行结束
gets(ch);
//如果你的C++版本较高,gets是不被支持的
fgets(ch,100,stdin);
输出
输出比较简单,直接cout简单明了
//直接cout就可以了
cout<<ch<<"\n";
但是如果一个字符串"Hello\0Bob"会输什么呢,他其实只会输出Hello,因为他认为后面的是垃圾,即"Bob"是垃圾。
char ch[100]="Hello\0Bob";
cout<<ch<<"\n";
当然你也可以使用C语言的printf
printf("%s",ch);
//还有一个较短小的puts
puts(ch);
四、题目实践
那么学会这个我们就可以做题目了吗,当然是可以的,我们先来尝试输入输出题。
TZOJ5600: 打招呼,题目很简单,输入一个人的名字,对他打个招呼,但是他的名字可能含有空格。
#include <bits/stdc++.h>
using namespace std;
int main()
{
//不超过20个字符,但是最后一位要存储'\0',多开一位
char ch[25];
//包含空格,使用getline输入
cin.getline(ch,21);
//直接按格式输出即可
cout<<"Hello, "<<ch<<"!\n";
return 0;
}
TZOJ1420: 手机短号 也是基本的输入输出比较简单。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
//读入n组
cin >> n;
while (n--)
{
//不超过11个字符
char ch[12];
cin>>ch;
//最后5个字母就是从&ch[11-5]开始输出
cout << "6" << ch+(11-5) << "\n";
/*
//和上面等价
cout << "6" << &ch[11-5] << "\n";
//继续等价
cout<<"6";
for(int i=11-5;i<11;i++)
{
cout<<ch[i];
}
cout<<"\n";
*/
}
return 0;
}
但是你如果使用了getline会遇到困难,输出的东西很奇怪。这是因为输入组数N之后还有一个换行符没有被正常吃掉,我可以可以cin.get();也可以使用getchar();
** 使用一行读取前一定要看看之前是否会存在换行或空格 **
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
//读入n组
cin >> n;
cin.get();
while (n--)
{
//不超过11个字符
char ch[12];
cin.getline(ch,12);
//最后5个字母就是从&ch[11-5]开始输出
cout << "6" << ch+(11-5) << "\n";
}
return 0;
}
我们怎么知道输入的有多长呢,可以用strlen函数。
#include <bits/stdc++.h>
using namespace std;
int main()
{
char s[100];
cin>>s;
//在函数里放入字符数组s作为参数
int l=strlen(s);
/*
//和strlen等价,也可以求出长度
l=0;
for(int i=0;s[i]!='\0';i++)
{
l++;
}
*/
cout<<l<<"\n";
return 0;
}
字符串遍历
我们需要用字符串的方式进行读取,读取了之后我们可以一个一个进行访问。
#include <bits/stdc++.h>
using namespace std;
int main()
{
char s[100];
cin>>s;
//1.直接按照字符串的末尾是'\0'进行遍历
//当然'\0'是0,!=0也可以直接舍去
for(int i=0;s[i]!='\0';i++)
{
/*
你要执行的代码
*/
}
//2.按照长度进行遍历
//在函数里放入字符数组s作为参数,读取s的长度
int l=strlen(s);
for(int i=0;i<l;i++)
{
/*
你要执行的代码
*/
}
return 0;
}
二维数组读入
TZOJ5104: 说反话 这题可以用二维数组解决,一维数组不带框([]),二维数组需要一个
#include <bits/stdc++.h>
using namespace std;
int main()
{
//一个字母一个空格,最多80/2=40个单词
//最少一个单词
char s[40][81];
//记录单词个数,初始为0
int n=0;
//循环读入所有单词
while(cin>>s[n])n++;
//倒着输出所有单词
for(int i=n-1;i>=0;i--)
{
//如果不是第一个,输出空格
if(i!=n-1)cout<<" ";
cout<<s[i];
}
cout<<"\n";
return 0;
}
如何比较大小呢,可以用strcmp(这里的比较大小为字典序),如果相等返回为0,小于返回负数,大于返回正数。
字典序顾名思义就是在字典里的先后,如果我从前往后翻一个字典,字典序小的一定会被我先翻到,即"abandon"一定在"abort"前面。
你可以猜猜下列程序的larger的结果,当然你可可以在本地电脑运行一下。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int larger;
char s1[20]="abandon",s2[20]="abort";
larger=strcmp(s1,s2);
cout<<"abandon和abort的大小\t"<<larger<<"\n";
char s3[20]="abandon",s4[20]="aban";
larger=strcmp(s3,s4);
cout<<"abandon和aban的大小\t"<<larger<<"\n";
char s5[20]="abandon",s6[20]="Abandon";
larger=strcmp(s5,s6);
cout<<"abandon和Abandon的大小\t"<<larger<<"\n";
char s7[20]="hello, Bob",s8[20]="hello, Alice";
larger=strcmp(s7,s8);
cout<<"hello, Bob和hello, Alice的大小\t"<<larger<<"\n";
char s9[20]="hello, Bob",s10[20]="hello, Bob";
larger=strcmp(s9,s10);
cout<<"hello, Bob和hello, Bob的大小\t"<<larger<<"\n";
return 0;
}
你有没有猜对呢?
"abandon"<"abort"是因为前两个字母相等,比较第三位'a'<'o'。
"abandon">"aban"是因为"abandon"比较长,比较第五位'd'>'\0'
"abandon">"Abandon"是因为ASCII值小写比大写的值大,比较第一位'a'>'A'
"hello, Bob">"hello, Alice"有空格依旧可以比较,比较第八位'B'>'A'
"hello, Bob"和"hello, Bob"两者完全相等。
五、字符串string类型
string是c++内部已经帮我们实现好的字符串类模版,他是不定长的,可以随时赋值。
#include <bits/stdc++.h>
using namespace std;
int main()
{
//直接按照字符串初始化
string s="12212";
//可以再次被赋值
s="1213";
//可以直接用其他变量赋值
string str1=s;
return 0;
}
他也可以进行+运算和比较大小运算,也可以直接获得长度。他的输入和输出和数组一样,只有读取一行的操作略作改变,调用c语言输出需要转换一下。
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s1="aba";
string s2="12";
//把s1和s2拼接起来
s2=s1+s2;
//cout<<s2<<"\n";
//直接用比较运算符进行比较
if(s1>s2)cout<<"s1>s2\n";
if(s1<s2)cout<<"s1<s2\n";
if(s1==s2)cout<<"s1==s2\n";
//获取长度
int l=s1.length();
//读取一行
getline(cin,s1);
//调用c_str使用c语言输出
printf("%s\n",s1.c_str());
return 0;
}
六、练习
可以练习的题目
1.TZOJ3714: 最简单的题
2.TZOJ5476: 学习之签到
3.TZOJ1166: 大小写转换
4.TZOJ4574: 转换大写
5.TZOJ1095: 保留字母
6.TZOJ5355: 星期几
7.TZOJ1171: 字符逆序
8.TZOJ5014: 说反话
9.TZOJ2878: 美食节之摆摊位(二)
10.TZOJ2961: 代码抄袭
11.TZOJ5611: 计算差异度
12.TZOJ5614: 请输入正确的名字
13.TZOJ5357: 字符串替换
14.TZOJ1055: 简单密码破解
15.TZOJ1379: C语言合法标识符
16.TZOJ1495: C语言实验题――统计子串
17.TZOJ4644: 展开字符串
18.TZOJ3457: 最小新数
19.TZOJ2625: 电话号码
20.TZOJ3212: Pig在哪里
21.TZOJ5133: U的爱慕者
22.TZOJ1484: C语言实验题――字符串排序
23.TZOJ5136: 隧道有多长
24.TZOJ1351: 单词数
25.TZOJ5019: 福尔摩斯的约会
26.TZOJ1426: 剪花布条
27.TZOJ1356: A == B ?
本文来自博客园,作者:暴力都不会的蒟蒻,转载请注明原文链接:https://www.cnblogs.com/BobHuang/p/12740540.html