《算法笔记》学习日记——3.6 字符串处理

3.6 字符串处理

Codeup Contest ID:100000580

问题 A: 字符串连接

题目描述
不借用任何字符串库函数实现无冗余地接受两个字符串,然后把它们无冗余的连接起来。
输入
每一行包括两个字符串,长度不超过100。
输出
可能有多组测试数据,对于每组数据,
不借用任何字符串库函数实现无冗余地接受两个字符串,然后把它们无冗余的连接起来。
输出连接后的字符串。
样例输入

abc def

样例输出

abcdef

思路
思路比较简单,因为输入的一行确定有两个字符串,所以比较好办:分别把两个字符串存入到两个char数组当中,然后再分别用for循环遍历两个数组,计算它们的长度,记为len1、len2,然后再开一个数组,分别把两个字符串的每个元素复制进去即可。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char str1[200]={0};
	while(scanf("%s", str1) != EOF){
		char str2[200]={0};
		scanf("%s", str2);
		char str1plusstr2[500]={0};
		int len1, len2;
		len1 = len2 = 0;
		for(int i=0;;i++){
			if(str1[i]=='\0') break;
			else len1++;
		}
		for(int i=0;;i++){
			if(str2[i]=='\0') break;
			else len2++;
		}
		for(int i=0;i<len1+len2;i++){
			if(i<len1){
				str1plusstr2[i] = str1[i];
			}
			else str1plusstr2[i] = str2[i-len1];
		}
		for(int i=0;i<len1+len2;i++) printf("%c", str1plusstr2[i]);
		printf("\n");
	}
	return 0;
}

问题 B: 首字母大写

题目描述
对一个字符串中的所有单词,如果单词的首字母不是大写字母,则把单词的首字母变成大写字母。
在字符串中,单词之间通过空白符分隔,空白符包括:空格(’ ‘)、制表符(’\t’)、回车符(’\r’)、换行符(’\n’)。
输入
输入一行:待处理的字符串(长度小于100)。
输出
可能有多组测试数据,对于每组数据,
输出一行:转换后的字符串。
样例输入

if so, you already have a google account. you can sign in on the right.

样例输出

If So, You Already Have A Google Account. You Can Sign In On The Right.

思路
首先这题不能用scanf()来读取字符串,因为scanf会吞空格,就算用scanf(%[^\n])来读取一行字符串,那也不能连续输入(输入一组输出一组即结束),所以需要用gets()来读取。
此外,这题主要有两个比较坑的点需要考虑:①并不保证第一位一定是字母,也有可能是空白符,所以不可以直接str1[0] -= 32,就算是第一位也是要加个判断条件的;②最后一位也不能保证一定是标点符号,如果是空白符,如果对下一位直接-=32,就会造成输出错误答案(因为字符串最后一位的下一位是’\0’),所以这里也需要加个判断条件,如果是’\0’就break,如果不是才执行小写字母转大写的操作。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char str1[200]={0};
	int i=0;
	while(gets(str1)){
		for(int i=0;i<strlen(str1);i++){
			if(str1[0]>='a'&&str1[0]<='z'){
				str1[0] -= 32;
			}
			if(str1[i]==' '||str1[i]=='\t'||str1[i]=='\r'||str1[i]=='\n'){
				if(str1[i+1]=='\0') break;
				else{
					if(str1[i+1]>='A'&&str1[i+1]<='Z') continue;
					else str1[i+1] -= 32;	
				}
			}
		}
		printf("%s\n", str1);
		memset(str1, 0, sizeof(str1));
	}
	return 0;
}

问题 C: 字符串的查找删除

题目描述
给定一个短字符串(不含空格),再给定若干字符串,在这些字符串中删除所含有的短字符串。
输入
输入只有1组数据。
输入一个短字符串(不含空格),再输入若干字符串直到文件结束为止。
输出
删除输入的短字符串(不区分大小写)并去掉空格,输出。
样例输入

in
#include 
int main()
{

printf(" Hi ");
}

样例输出

#clude
tma()
{

prtf("Hi");
}

提示
注:将字符串中的In、IN、iN、in删除。
思路
写这种字符串类型题的时候思路一定要清晰,并且代码最好简洁清晰易懂,不然是非常折磨人的……
我一开始过了样例,但是一直是答案错误50,因为不知道还有哪几个测试点没过,所以就乱改一通,然后代码越改越长,到后来自己也看不懂了。直到后来狠下心把之前的代码全删了,重新理清思路写了一遍,然后输入特殊的字符串慢慢调试找到错误,就AC了,虽然花了一下午,但收获还是很大的。

首先,这一题既可以读入全部数据,然后一并输出,也可以读入一行输出一行,我这里选择了读入一行输出一行,这样代码比较简洁,debug起来也非常方便。
然后就是一定要用gets()读入字符串,不能用scanf(),因为scanf()遇到空格会停止,而后续要处理的字符串又肯定会含有空格。

在我的代码中,判断子串的手段是:先判断读入的字符是否是子串中的首个字符,如果不是,则直接存入将要输出的字符串tmp中,如果是,则将其压入judge字符串中,并continue进入下一轮循环。在下一轮循环中,如果下一个字符还是匹配子串的,则继续压入judge字符串,否则,将judge字符串通过strcat()函数连接到tmp上,并且要i–(比如子串是in,输入的是iini,如果不进行i–的话,下一次循环将从n开始,那么n又不和子串的首字符i匹配,就会造成错误,所以应该从第二个i开始,继续判断是否和子串匹配)。

还有一个问题是要处理最后的字符,如果最后的字符不是子串中的,那倒没什么问题,如果是子串中的字符,尤其是和子串首字符一样的话,就会被压入judge,那么我们就要在for循环外面把已经压入judge但是并未输出的字符串一并加到tmp后面进行输出。

具体看代码的注释吧,字符串处理的问题太绕了,思考不清楚会花很久的时间(比如我……)。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char a[1000]={0};//子串 
	char judge[1000]={0};//存a判断过的字符 
	gets(a);
	char temp[1000]={0};//存输入的要判断的字符串 
	char tmp[1000]={0};//存已经删除完毕的字符串 
	while(gets(temp) != NULL){
		int len = 0;//len是tmp的长度
		int k = 0;//k用来判断是否和子串完全匹配
		for(int i=0;i<strlen(temp);i++){
			if(temp[i]==' ') continue;
			if(abs(temp[i]-a[k])==32||temp[i]==a[k]){
				judge[k] = temp[i];
				k++;
				if(k==strlen(a)){ //如果k的值和子串a的长度相等,则完全匹配
					memset(judge, 0, sizeof(judge));//清空judge
					k = 0;//清空k
				}
				continue;
			}
			else if(strlen(judge)>0){ //如果不完全匹配子串,则将已含有的元素输出
				strcat(tmp, judge);
				len += strlen(judge);
				memset(judge, 0, sizeof(judge));//输出完之后记得也要清空
				k = 0;
				i--;//这里一定要把i减一位,不然当前的temp[i]会丢失,因为judge里存的是之前的字符
			}
			else{ //如果完全不匹配子串,则直接进行输出
				tmp[len] = temp[i];
				len++;
			}
		}
		if(strlen(judge)>0) strcat(tmp, judge);//这里是防止最后一位是子串中的首字符(比如iini中的最后一位i,会放入judge中,但是需要把它连到tmp上)
		printf("%s\n", tmp);
		memset(judge, 0, sizeof(judge));
		memset(temp, 0, sizeof(temp));
		memset(tmp, 0, sizeof(tmp));
	}
	return 0;
} 

问题 D: 单词替换

题目描述
输入一个字符串,以回车结束(字符串长度<=100)。该字符串由若干个单词组成,单词之间用一个空格隔开,所有单词区分大小写。现需要将其中的某个单词替换成另一个单词,并输出替换之后的字符串。
输入
多组数据。每组数据输入包括3行,
第1行是包含多个单词的字符串 s,
第2行是待替换的单词a,(长度<=100)
第3行是a将被替换的单词b。(长度<=100)
s, a, b 最前面和最后面都没有空格。
输出
每个测试数据输出只有 1 行,
将s中所有单词a替换成b之后的字符串。
样例输入

I love Tian Qin
I
You

样例输出

You love Tian Qin

思路
我的思路比较简单,实现起来也很清晰。就是先用gets()函数读入输入的一行字符串,然后通过一个for循环的操作,将其按空格切割并存储在一个字符串数组中(比如s[0]=“I”, s[1]=“love”, s[2]=“Tian”, s[3]=“Qin”)。然后只要遍历这个字符串数组,找到其和字符串a相匹配的值(用strcmp函数),并将之替换成b(用strcpy函数)即可。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char temp[101]={0};
	char s[101][101]={0};
	char a[101]={0};
	char b[101]={0};
	while(gets(temp) != NULL){
		gets(a);
		gets(b);
		int len, m;
		len = m = 0;//len记录字符串的个数(字符串数组的一维),m记录每个字符串的长度(字符串数组的二维) 
		for(int i=0;i<strlen(temp);i++){
			if(temp[i]==' '){
				len++;
				m = 0;
				continue;
			}
			else{
				s[len][m] = temp[i];
				m++;
			}
		}//执行完之后字符串的个数应该是len+1(0~len) 
		for(int i=0;i<=len;i++){
			if(strcmp(s[i], a)==0) strcpy(s[i], b);//如果s[i]和a匹配,则此处将s[i]替换成b 
		}
		for(int i=0;i<=len;i++){
			if(i==0) printf("%s", s[i]);
			else printf(" %s", s[i]);
		}
		printf("\n");
		memset(temp, 0, sizeof(temp));
		memset(a, 0, sizeof(a));
		memset(b, 0, sizeof(b));
		for(int i=0;i<=len;i++) memset(s[i], 0, sizeof(s[i]));
	} 
	return 0;
} 

问题 E: 字符串去特定字符

题目描述
输入字符串s和字符c,要求去掉s中所有的c字符,并输出结果。
输入
测试数据有多组,每组输入字符串s和字符c。
输出
对于每组输入,输出去除c字符后的结果。
样例输入

goaod
a

样例输出

good

思路
这题和问题C类似,但是要比问题C简单得多,因为问题C要求删除的是子串,而这题只要删除一个字符就好了。
这题需要注意的一点是,c字符可能可以是空格(因为之前用scanf读不含空格的字符串一直是答案错误50%,也就是说,字符串s中是有可能含空格的,所以要用gets()来读,不能用scanf),另外,用scanf读完字符c之后,一定要加getchar()吃掉回车符,否则下一次会出错。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char s[1000]={0};
	while(gets(s) != NULL){
		char temp[1000]={0};
		char c;
		scanf("%c", &c);
		getchar();
		int len = 0;
		for(int i=0;i<strlen(s);i++){
			if(s[i]==c) continue;//和字符c相等,则跳过
			else{
				temp[len] = s[i];//否则就赋值给temp
				len++;
			}
		}
		printf("%s\n", temp);
		memset(s, 0, sizeof(s));
		memset(temp, 0, sizeof(temp));
	}
	return 0;
} 

问题 F: 数组逆置

题目描述
输入一个字符串,长度小于等于200,然后将数组逆置输出。
输入
测试数据有多组,每组输入一个字符串。
输出
对于每组输入,请输出逆置后的结果。
样例输入

tianqin

样例输出

niqnait

提示
注意输入的字符串可能会有空格。
思路
这题非常简单,将输入用gets()读入进一个字符串中(不要用scanf(),因为scanf()碰到空格就停止读入了,而gets()是读一行),然后倒序输出即可。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char str[201];
	while(gets(str) != NULL){
		for(int i=strlen(str)-1;i>=0;i--) printf("%c", str[i]);
		printf("\n");
		memset(str, 0, sizeof(str));
	}
	return 0;
} 

问题 G: 比较字符串

题目描述
输入两个字符串,比较两字符串的长度大小关系。
输入
输入第一行表示测试用例的个数m,接下来m行每行两个字符串A和B,字符串长度不超过50。
输出
输出m行。若两字符串长度相等则输出A is equal long to B;若A比B长,则输出A is longer than B;否则输出A is shorter than B。
样例输入

2
abc xy
bbb ccc

样例输出

abc is longer than xy
bbb is equal long to ccc

思路
这题也比较简单,直接用自带的strlen函数比较一下就行了。
不过要注意小坑,这里是输入m行,输出m行,所以不能用while(m–),要用for循环,而且要将每一行的字符串存储进字符串数组,我这里是这样处理的:每次比较完之后,根据结果用strcat函数将三个部分串联在一起(字符串A+判断字符串+字符串B)。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	int m;
	char str[100][100];
	char equal[100]=" is equal long to ";
	char longer[100]=" is longer than ";
	char shorter[100]=" is shorter than ";
	while(scanf("%d", &m) != EOF){
		for(int i=0;i<m;i++){
			char str1[100]={0};
			char str2[100]={0};
			scanf("%s%s", str1, str2);
			if(strlen(str1)==strlen(str2)){
				strcat(str[i], str1);
				strcat(str[i], equal);
				strcat(str[i], str2);
			}
			if(strlen(str1)>strlen(str2)){
				strcat(str[i], str1);
				strcat(str[i], longer);
				strcat(str[i], str2);
			}
			if(strlen(str1)<strlen(str2)){
				strcat(str[i], str1);
				strcat(str[i], shorter);
				strcat(str[i], str2);
			}
		}
		for(int i=0;i<m;i++) printf("%s\n", str[i]);
		for(int i=0;i<m;i++) memset(str[i], 0, sizeof(str[i]));
	}
	return 0;
} 

问题 H: 编排字符串

题目描述
请输入字符串,最多输入4 个字符串,要求后输入的字符串排在前面,例如
输入:EricZ
输出:1=EricZ
输入:David
输出:1=David 2=EricZ
输入:Peter
输出:1=Peter 2=David 3=EricZ
输入:Alan
输出:1=Alan 2=Peter 3=David 4=EricZ
输入:Jane
输出:1=Jane 2=Alan 3=Peter 4=David
输入
第一行为字符串个数m,接下来m行每行一个字符床,m不超过100,每个字符床长度不超过20。
输出
输出m行,每行按照样例格式输出,注意用一个空格隔开。
样例输入

5
EricZ
David
Peter
Alan
Jane

样例输出

1=EricZ
1=David 2=EricZ
1=Peter 2=David 3=EricZ
1=Alan 2=Peter 3=David 4=EricZ
1=Jane 2=Alan 3=Peter 4=David

思路
这一题思路清晰的话其实也不难,我的想法是首先把"1="、" 2="、" 3="、" 4="这四个字符串预处理在一个字符串数组中,这样的话,下标正好对应一个数,也方便动态处理。

然后将输入的五个字符串存入一个字符串数组之中,再用for循环遍历m次,每次将处理好的字符串给tmp[i](tmp也是一个字符串数组,用于存放操作结果),最后再遍历m次tmp字符串数组逐一输出即可。

至于怎么处理呢,我认为在每次对一个tmp[i]进行操作时,可以再用一次for循环,从当前对应的名字往前找(比如处理tmp[1]的时候,可以从David往前找到EricZ;又比如处理tmp[4]的时候,可以从Jane往前到Alan,再往前到Peter……最后到David终止),然后每次用strcat串联相应的信息即可。

最后记得在i>=4的时候,改变一下循环条件即可,具体看代码吧!
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char s[100][100];//输入的字符串
	char tmp[100][100];//输出的字符串
	char number[10][10]={"1=", " 2=", " 3=", " 4="};//预处理,记得2、3、4之前要加空格
	int m;
	while(scanf("%d", &m) != EOF){
		for(int i=0;i<m;i++) scanf("%s", s[i]);
		for(int i=0;i<m;i++){
			int k = 0;//每一层的对tmp操作时都要把k归零
			if(i>=4){
				for(int j=i;j>i-4;j--){ //当i>=4的时候,往前最多到i-4+1的位置
					strcat(tmp[i], number[k]);//先连接数字和等号
					strcat(tmp[i], s[j]);//再连接对应的名字
					k++;//k自加1,下次连接的数字和等号将是下一个值
				}
			}
			else{
				for(int j=i;j>=0;j--){ //如果i<4,那么一直往前到0的位置
					strcat(tmp[i], number[k]);
					strcat(tmp[i], s[j]);
					k++;
				}
			}
		}
		for(int i=0;i<m;i++) printf("%s\n", tmp[i]);
		for(int i=0;i<100;i++) memset(s[i], 0, sizeof(s[i]));
		for(int i=0;i<100;i++) memset(tmp[i], 0, sizeof(tmp[i]));
	}
	return 0;
} 

问题 I: 【字符串】回文串

题目描述
读入一串字符,判断是否是回文串。“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
输入
一行字符串,长度不超过255。
输出
如果是回文串,输出“YES”,否则输出“NO”。
样例输入

12321

样例输出

YES

思路
这题比较简单,同时从头从尾开始遍历,然后比较对应的字符是否相等即可,如果相等,就是回文串,如果不等,就不是回文串。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
int main(){
	char s[300];
	while(scanf("%s", s) != EOF){
		int k = 0;
		for(int i=0, j=strlen(s)-1;i<strlen(s), j>=0;i++, j--){
			if(s[i]==s[j]) k++;
		}
		if(k==strlen(s)) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
} 

小结

总的来说,字符串的题相对于我这种小白来说难度上升了不少,但是只要厘清思路,不骄不躁,慢慢把思路用代码实现,其实也是不难的。主要的坑就在于题目给出的字符串带不带空格,或者说带不带别的什么符号,这个时候就要弄清楚scanf()和gets()之间的区别,如果有空格的话,就一定要用gets()来读取,并且一定是while(gets(str) != NULL),才能做到读到文件尾部结束。

最难的题就是就是问题C了,我昨天做了整整一个下午才AC,现在回过头来看看,也不是说难度特别大,我认为自己最大的问题就是把代码写得过于冗杂,以至于在每次答案错误之后就进行一点修改,直到把代码改到了八九十行,自己看起来也费劲(后来WA的时候完全没有心思再去跟着程序走一遍看看哪儿错了,因为实在是太长了),这个时候我建议还是把思路弄弄清楚:子串怎么判别,怎么删除,完全匹配该怎么做,不完全匹配又该怎么做,完全不匹配的情况下又该如何,这三个地方想清楚了,把代码删掉,重新按着清晰版的思路写一遍,遇到问题再对症下药,就会很快AC了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值