字符数组及练习
C++中的字符串
字符串的三种形式
- 用双引号括起来的字符串常量,如"chinga","C++ program"等。
- 字符数组:存放于字符数组,以’\0’结尾。
- string类型:C++标准模板库里的一个类,用于专门处理字符串(略)
字符串常量
字符串常量占据内存的字节数等于字符串中字符数目加1,多出来的是结尾字符’\0’。
字符串"C program" 在内存中的布局:
C | p | r | o | g | r | a | m | \0 |
---|
字符串的长度不包含’\0’
"“也是合法的字符串常量,称为“空串”,空串仍然会占据一个字节的存储空间,存放’\0’。
如果字符串常量中包含双引号,则双引号应写为‘”’。而字符串中出现时,须连写两次,变‘\’。例如:
cout<<"He said: \"I am a stu\\dent.\"";
He said:"I am a stu\ent."
字符数组
一维字符数组的定义
char 数组名[常量表达式] 如:char name[10];
也可以在定义时初始化
如:char name[10] = {‘J’,‘o’,‘h’,‘n’}
或者char [name] = {John}
在内存中的布局
J | o | h | n | \0 |
---|
字符数组的末尾都会有一个’\0’,表示字符数组的结束。
用一维char数组存放字符串
包含’\0’字符的一维char数组,就是一个字符串。其中存放的字符串即为’\0’前面的字符组成。
用char数组存放字符串,数组元素个数应该至少为字符串长度+1。
char数组的内容,可以在初始化时设定,也可以用C++库函数进行修改,还可以用对数组元素赋值的办法任意改变其中的某个字符。
字符数组同样可以用cout、printf输出,用cin、scanf读入。用cin、printf将字符串读入字符数组时,会自动在字符数组中字符串的末尾加上’\0’。
字符数组的读入
- scanf(“%s”,字符数组名)
- 用scanf可以将字符串读入字符数组
- scanf会自动添加结尾的’\0’
- scanf读入到空格为止
char line[100];
scanf("%s",line);
printf("%s",line);
Fox River
Fox
- cin读入,与使用scanf读入一样,会自动在末尾加"\0"
如上面的:cin>>line
scanf与cin读入
- 在数组长度不足的情况下,scanf可能导致数组越界
char line[5];
scanf("%s",line);
若输入“12345”,则数组越界!
- cin输入字符串的情况和scanf相同
char line[5];
cin>>line;
gets()
gets(char buf[]);
读入一行,自动添加’\0’
回车换行符不会写入buf,但是会从输入流中去掉。可能导致数组越界!
char s[10];
while(gets(s)){
printf("%s\n",s);
}
A b c(Enter)
A b c
12 34(Enter)
12 34
^z(Enter)
注意:由于gets()可能导致数组越界,所以必须保证buf足够大
3种读入方式比较:scanf与cin遇到空格及回车结束,gets遇到回车结束,cin读入速度比scanf慢;gets可以读整行,但注意越界。
字符数组的输出
- printf(“%s”,字符数组名)
- cout<<字符数组名
- puts(字符数组名)
分别对应3种输入,注意输出时遇到"\0"结束
m | y | i | s | \0 | I | m | \0 |
---|
无论用哪种输出都只会输出my is
字符串库函数
- 使用字符串函数需要#include
- 字符串函数都根据’\0’来判断字符串结尾
- 形参为char []类型,则实参可以是char数组或字符串常量
字符串拷贝
strcpy(char [ ] dest,char[ ] src);//拷贝src到dest
字符串比较大小
int strcmp(char [ ] s1, char [ ] s2);//返回0则相等
求字符串长度
int strlen(char [ ] s );//不算结尾的'\0'
字符串拼接
strcat(char [ ] s1,char [ ] s2);//拼接到s1后面
字符串转成大写
strupr(char [ ] );
字符串转成小写
strlwr(char [ ]);
例题:P1125 [NOIP2008 提高组] 笨小猴
[NOIP2008 提高组] 笨小猴
题目描述
笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!
这种方法的具体描述如下:假设 maxn \text{maxn} maxn 是单词中出现次数最多的字母的出现次数, minn \text{minn} minn 是单词中出现次数最少的字母的出现次数,如果 maxn − minn \text{maxn}-\text{minn} maxn−minn 是一个质数,那么笨小猴就认为这是个 Lucky Word,这样的单词很可能就是正确的答案。
输入格式
一个单词,其中只可能出现小写字母,并且长度小于 100 100 100。
输出格式
共两行,第一行是一个字符串,假设输入的的单词是 Lucky Word,那么输出 Lucky Word
,否则输出 No Answer
;
第二行是一个整数,如果输入单词是 Lucky Word
,输出
maxn
−
minn
\text{maxn}-\text{minn}
maxn−minn 的值,否则输出
0
0
0。
样例 #1
样例输入 #1
error
样例输出 #1
Lucky Word
2
样例 #2
样例输入 #2
olympic
样例输出 #2
No Answer
0
提示
【输入输出样例 1 解释】
单词 error
中出现最多的字母
r
\texttt r
r 出现了
3
3
3 次,出现次数最少的字母出现了
1
1
1 次,
3
−
1
=
2
3-1=2
3−1=2,
2
2
2 是质数。
【输入输出样例 2 解释】
单词 olympic
中出现最多的字母
i
\texttt i
i 出现了
1
1
1 次,出现次数最少的字母出现了
1
1
1 次,
1
−
1
=
0
1-1=0
1−1=0,
0
0
0 不是质数。
#include<cstdio>
#include<cstring>//strlen函数
using namespace std;
char s[110];
const int prime[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};//打表
int sum[26];//记录和
int main(){
int maxn=0,minn=110;//最大值和最小值,注意初始化
scanf("%s",s);//用scanf读入字符数组时,注意不用加&
int len=strlen(s);//求字符串长度的函数
for(int i=0;i<len;i++){//遍历字符串
sum[s[i]-'a']++;//见上
}
for(int i=0;i<26;i++){//遍历26个字母
if(sum[i]>maxn) maxn=sum[i];//比较maxn
if(sum[i]>0&&sum[i]<minn) minn=sum[i];//比较minn,只比较出现过的字母
}
for(int i=0;i<25;i++){//遍历25个质数
if(maxn-minn==prime[i]){//满足条件了
printf("Lucky Word\n%d",maxn-minn);//输出,注意格式
return 0;//返回
}
}
printf("No Answer\n0");//不满足的情况,注意输出0
return 0;//华丽结束
}
练习题-[2007年NOIP提高组] 字符串的展开
在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h”或者“4-8”的字串,我们就把它当作一种简写,输出时,用连续递增的字母获数字串替代其中的减号,即,将上面两个子串分别输出为“defgh”和“45678”。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:
(1) 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号“-”,减号两侧同为小写字母或同为数字,且按照ASCII码的顺序,减号右边的字符严格大于左边的字符。
(2) 参数p1:展开方式。p1=1时,对于字母子串,填充小写字母;p1=2时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1=3时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号“*”来填充。
(3) 参数p2:填充字符的重复个数。p2=k表示同一个字符要连续填充k个。例如,当p2=3时,子串“d-h”应扩展为“deeefffgggh”。减号两边的字符不变。
(4) 参数p3:是否改为逆序:p3=1表示维持原来顺序,p3=2表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当p1=1、p2=2、p3=2时,子串“d-h”应扩展为“dggffeeh”。
(5) 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:“d-e”应输出为“de”,“3-4”应输出为“34”。如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:“d-d”应输出为“d-d”,“3-1”应输出为“3-1”。
输入
输入包括两行:
第1行为用空格隔开的3个正整数,一次表示参数p1,p2,p3。
第2行为一行字符串,仅由数字、小写字母和减号“-”组成。行首和行末均无空格。
输出
输出只有一行,为展开后的字符串。
样例输入
1 2 1
abcs-w1234-9s-4zz
样例输出
abcsttuuvvw1234556677889s-4zz
提示
【限制】40%的数据满足:字符串长度不超过5
100%的数据满足:1<=p1<=3,1<=p2<=8,1<=p3<=2。字符串长度不超过100
未完成的代码
#include<iostream>
#include<cstring>
using namespace std;
string s;
char s1[101];
int main()
{
int p1,p2,p3,k;
cin>>p1>>p2>>p3;
cin>>s;
k=p2;//重复填的次数
for(size_t i=0;i<s.length();i++)
{
if(s[i]=='-')//修改字符-位置
{
//如果-的右边是左边的后继,就删除-
if(s[i+1] == s[i-1] + 1)
{
for(size_t j=i;j<s.length();j++)
s[j]=s[j+1];
}else {
//记录一下-号中间差哪些字符
int tmp1=s[i-1],tmp2=s[i+1];
for(int j=0;j<=tmp2-tmp1;j++)
s1[j]=++tmp1;//比如ab-f 差的就是cde将这几个字母放在s1数组中
if(p3==1)
{//原序
//for()
}else if(p3==2)
{//逆序
}
}
}
}
cout<<s<<endl<<s1;
return 0;
}
写到这个样子,我蒙了,先暂存一下,忙完这几天再来思考
#include<iostream>
#include<cstring>
using namespace std;
string s;//s是原字符串
char s1[101],ss[101];//s1-号处要插入的字符串,ss是运算后要插入-号处的字符串
int main()
{
int p1,p2,p3,k;
cin>>p1>>p2>>p3;
cin>>s;
k=p2;//重复填的次数
for(size_t i=0;i<s.length();i++)
{
if(s[i]=='-')//修改字符-位置
{
//如果-的右边是左边的后继,就删除-
if(s[i+1] == s[i-1] + 1)
{
for(size_t j=i;j<s.length();j++)
s[j]=s[j+1];
}else {
//记录一下-号中间差哪些字符
int tmp1=s[i-1],tmp2=s[i+1];
for(int j=0;j<=tmp2-tmp1;j++)
s1[j]=++tmp1;//比如ab-f 差的就是cde将这几个字母放在s1数组中
//处理参数p1
if(p1 == 1)
{//处理s1字符串为小写字母
for(size_t j=0;j<strlen(s1);j++)
{
if((int)s1[j]>=65 && (int)s1[j]<=65+26)
s1[j]+=32;
}
}else if(p1 == 2)
{//处理s1字符串为大写字母
for(size_t j=0;j<strlen(s1);j++)
{
if((int)s1[j]>=97 && (int)s1[j]<=97+26)
s1[j]-=32;
}
}else if(p1 == 3)
{//字符串全替换成*号
for(size_t j=0;j<strlen(s1);j++)
s1[j]='*';
}
//处理参数p2,处理后的字符串暂时存放在ss中
for(size_t j=0;j<strlen(s1);j++)
{
for(int m = 1; m <= k; m++)
ss[k*j+m-1]=s1[j];
}
//处理参数p3
if(p3==1)
{//原序
//在原字符串第i位上开始插入ss字符串
//i位置后的字符串后移strlen(ss)个位置
for(size_t j=s.length();j>=i;j--)
s[j+strlen(ss)] = s[j];//这个会越界?
cout<<s<<endl;
for(size_t j=i;j<i+strlen(ss);j++)
for(size_t m=0;m<strlen(ss);m++)
s[j]=ss[m];
}else if(p3==2)
{//逆序
}
}
}
}
cout<<s;
//for(size_t i=0;i<strlen(results);i++)
//cout<<results[i];
return 0;
}
不甘心,没解决好,又写了一次,这一次出现的问题是第一个-号正确,第二个-号位置不对
#include<iostream>
#include<cstring>
using namespace std;
char t1[101],t2[101];
string s,rs;
int main()
{
int p1,p2,p3,ins;
cin>>p1>>p2>>p3;
cin>>s;
int k=p2;
for(size_t i = 0;i < s.length();i++)
{
if(s[0] == '-' || s[s.length()-1]=='-') break;//阻止越界的情况产生
if(s[i] == '-' && s[i+1]>s[i-1]+1)
{
ins=i;
//s
int tmp1=s[i-1],tmp2=s[i+1];
for(int j=0;j<=tmp2-tmp1;j++)
t1[j]=++tmp1;//比如ab-f 差的就是cde将这几个字母放在t1数组中
//处理参数p1
if(p1 == 1)
{//处理s1字符串为小写字母
for(size_t j=0;j<strlen(t1);j++)
{
if((int)t1[j]>=65 && (int)t1[j]<=65+26)
t1[j]+=32;
}
}else if(p1 == 2)
{//处理s1字符串为大写字母
for(size_t j=0;j<strlen(t1);j++)
{
if((int)t1[j]>=97 && (int)t1[j]<=97+26)
t1[j]-=32;
}
}else if(p1 == 3)
{//字符串全替换成*号
for(size_t j=0;j<strlen(t1);j++)
t1[j]='*';
}
//处理参数p2,处理后的字符串暂时存放在t2中
for(size_t j=0;j<strlen(t1);j++)
{
for(int m = 1; m <= k; m++)
t2[k*j+m-1]=t1[j];
}
//清除-号,插入字符串
s.erase(ins,1);
s.insert(ins,t2);
}
}//end for
cout<<s<<endl;
return 0;
}
1 2 1
abcs-w1234-9s-4zz
abcsttuuvvw12345566779s-4zz
--------------------------------
Process exited after 5.898 seconds with return value 0
请按任意键继续. . .
//加了p3参数
#include<iostream>
#include<cstring>
using namespace std;
char t1[101],t2[101],t3[101];
string s,rs;
int main()
{
int p1,p2,p3,ins;
cin>>p1>>p2>>p3;
cin>>s;
int k=p2;
for(size_t i = 0;i < s.length();i++)
{
if(s[0] == '-' || s[s.length()-1]=='-') break;//阻止越界的情况产生
if(s[i] == '-' && s[i+1]>s[i-1]+1)
{
ins=i;
//s
int tmp1=s[i-1],tmp2=s[i+1];
for(int j=0;j<=tmp2-tmp1;j++)
t1[j]=++tmp1;//比如ab-f 差的就是cde将这几个字母放在t1数组中
//处理参数p1
if(p1 == 1)
{//处理s1字符串为小写字母
for(size_t j=0;j<strlen(t1);j++)
{
if((int)t1[j]>=65 && (int)t1[j]<=65+26)
t1[j]+=32;
}
}else if(p1 == 2)
{//处理s1字符串为大写字母
for(size_t j=0;j<strlen(t1);j++)
{
if((int)t1[j]>=97 && (int)t1[j]<=97+26)
t1[j]-=32;
}
}else if(p1 == 3)
{//字符串全替换成*号
for(size_t j=0;j<strlen(t1);j++)
t1[j]='*';
}
//处理参数p2,处理后的字符串暂时存放在t2中
for(size_t j=0;j<strlen(t1);j++)
{
for(int m = 1; m <= k; m++)
t2[k*j+m-1]=t1[j];
}
//清除-号,插入字符串
s.erase(ins,1);
if(p3==1)
s.insert(ins,t2);
else if(p3==2)
{
for(size_t i=0;i<strlen(t2);i++)
t3[i]=t2[strlen(t2)-1-i];
s.insert(ins,t3);
}
}
}//end for
//这里处理p3了
cout<<s;
return 0;
}
看了B站视频,参考涂老师的讲解重新写了一遍代码:
#include<iostream>
#include<cstring>
using namespace std;
int p1,p2,p3;
string s;
bool isabc(char a){//判断是否是小写字母
if(a >= 'a' && a <= 'a'+26) return true;
else return false;
}
bool isNum(char a){
if( a >= 48 && a <= 48+10) return true;
else return false;
}
bool isjh(char a){
if(a=='-') return true;else return false;
}
bool judge(char a,char b){//判断a和b是否同时为数字或字母
if(isdigit(a) && isdigit(b)) return true;
else if(isalpha(a) && isalpha(b)) return true;
return false;
}
void ins(char a,char b){ //插入中间差的那部分字母
if(p3 == 1){
for(char i = a+1; i < b;i++)
{//按插入规则进行输出
for(int j = 1;j <= p2 ;j++){
if(p1 == 1) cout<< (char)tolower(i);
else if(p1 == 2) cout<<(char)toupper(i);
else if(p1 == 3)cout<<'*';
}
}
}
if(p3 == 2){
for(int i= b-1; i > a;i--)
{//按插入规则进行输出
for(int j = 1;j <= p2 ;j++){
if(p1 == 1) cout<< (char)tolower(i);
else if(p1 == 2) cout<<(char)toupper(i);
else if(p1 == 3) cout<<'*';
}
}
}
}
int main()
{
cin>>p1>>p2>>p3>>s;
for(size_t i=0;i<s.length();i++)
{
if(isjh(s[i])){//如果遇到-号
if(s[i+1]-s[i-1] ==1 ) continue;
else if(s[i+1] <= s[i-1]){
cout<<s[i];continue;
}
else if(judge(s[i-1],s[i+1]) ){
//如果同为小写或同为数字
ins(s[i-1],s[i+1]);
continue;
}
}
cout<<s[i];
}
return 0;
}
小结,这题主要是思路要理清楚,当出现了不成功的情况要知道如何修改