2023年省信息技术教师培训day12(字符串)

C++中的字符串

字符串的三种形式

  1. 用双引号括起来的字符串常量,如"chinga","C++ program"等。
  2. 字符数组:存放于字符数组,以’\0’结尾。
  3. string类型:C++标准模板库里的一个类,用于专门处理字符串(略)

字符串常量

字符串常量占据内存的字节数等于字符串中字符数目加1,多出来的是结尾字符’\0’。
字符串"C program" 在内存中的布局:

Cprogram\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}
在内存中的布局

John\0

字符数组的末尾都会有一个’\0’,表示字符数组的结束。

用一维char数组存放字符串

包含’\0’字符的一维char数组,就是一个字符串。其中存放的字符串即为’\0’前面的字符组成。
用char数组存放字符串,数组元素个数应该至少为字符串长度+1。
char数组的内容,可以在初始化时设定,也可以用C++库函数进行修改,还可以用对数组元素赋值的办法任意改变其中的某个字符。
字符数组同样可以用cout、printf输出,用cin、scanf读入。用cin、printf将字符串读入字符数组时,会自动在字符数组中字符串的末尾加上’\0’。

字符数组的读入

  1. scanf(“%s”,字符数组名)
  • 用scanf可以将字符串读入字符数组
  • scanf会自动添加结尾的’\0’
  • scanf读入到空格为止
char line[100];
scanf("%s",line);
printf("%s",line);

Fox River
Fox

  1. 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可以读整行,但注意越界。

字符数组的输出

  1. printf(“%s”,字符数组名)
  2. cout<<字符数组名
  3. puts(字符数组名)
    分别对应3种输入,注意输出时遇到"\0"结束
myis\0Im\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} maxnminn 是一个质数,那么笨小猴就认为这是个 Lucky Word,这样的单词很可能就是正确的答案。

输入格式

一个单词,其中只可能出现小写字母,并且长度小于 100 100 100

输出格式

共两行,第一行是一个字符串,假设输入的的单词是 Lucky Word,那么输出 Lucky Word,否则输出 No Answer

第二行是一个整数,如果输入单词是 Lucky Word,输出 maxn − minn \text{maxn}-\text{minn} maxnminn 的值,否则输出 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 31=2 2 2 2 是质数。

【输入输出样例 2 解释】

单词 olympic 中出现最多的字母 i \texttt i i 出现了 1 1 1 次,出现次数最少的字母出现了 1 1 1 次, 1 − 1 = 0 1-1=0 11=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;
}

小结,这题主要是思路要理清楚,当出现了不成功的情况要知道如何修改
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值