C++面试笔记--字符串
基本上求职者进行笔试没有不考字符串的。字符串也是一种相对简单的数据结构,容易被考。事实上,字符创也是一个考验程序猿编程规范和编程习惯的重要考点。
1、替换空格:实现一个函数,把字符串中的每个空格替换成“%20”。例如输入“we are happy。”则输出“we%20are%20happy。”
答案:用一个空的字符串去替换%,2,0三个字符,那么原字符就会变长从而覆盖掉后面的字符,我么可以考虑创建一个新的字符串并在新的字符串上面做替换,那样就能够分配足够的内存了。在这里我们可以考虑从后往前去替换字符串。这里创建两个指针
1 #include<iostream> 2 #include <string> 3 #include<cstring> 4 #include<cstdio> 5 #include<stdlib.h> 6 using namespace std; 7 /* 8 算法原理:准备两个指针p1,p2;p1指向原始字符串的末尾,p2指向替换之后的字符串的末尾。接下来我们向前移动指针p1, 9 逐个把它指向的字符复制到p2指向的位置,知道碰到第一个空格为止, 10 算法:先遍历一次字符串,记录空格的数目,在计算新的字符数组的长度 11 在从后往前进行替换 12 */ 13 char *copystr(char *s) 14 { 15 int len=strlen(s);//字符串的长度 16 int blank=0;//表示空格的数目 17 if(len==0) 18 { 19 return NULL; 20 } 21 if(s==NULL) 22 { 23 return NULL; 24 } 25 26 for(int i=0; i<len; i++) //判断字符串,确定空格的数目,从而确定新的字符串所需的个数 27 { 28 if(s[i]==' ') 29 blank++; 30 } 31 if(blank==0) 32 return s; 33 int new_len=blank*2+len;//计算新的字符数组的长度(本来不该加一,但是最后的输出要求加一) 34 int l=new_len+1;//这里'\0'也算一个长度 35 char *str1; 36 for(int i=len; i>=0; i--)//算法:有空格就赋值,没有空格直接复制,从后往前复制 37 { 38 if(s[i]!=' ')//从后往前复制 39 { 40 str1[l-1]=s[i]; 41 l--; 42 } 43 else//转换空格 44 { 45 str1[l-1]='0'; 46 str1[l-2]='2'; 47 str1[l-3]='%'; 48 l-=3; 49 } 50 } 51 return str1; 52 } 53 int main() 54 { 55 static char str[10000000]; 56 gets(str); 57 char *dest = copystr(str); 58 if(dest != NULL) 59 puts(dest); 60 dest = NULL; 61 return 0; 62 63 }
1 #include<iostream> 2 #include <string> 3 #include<cstring> 4 #include<cstdio> 5 #include<stdlib.h> 6 using namespace std; 7 /* 8 算法原理:准备两个指针p1,p2;p1指向原始字符串的末尾,p2指向替换之后的字符串的末尾。接下来我们向前移动指针p1, 9 逐个把它指向的字符复制到p2指向的位置,知道碰到第一个空格为止, 10 算法:先遍历一次字符串,记录空格的数目,在计算新的字符数组的长度 11 在从后往前进行替换 12 */ 13 char *copystr(char *s) 14 { 15 int len=strlen(s);//字符串的长度 16 int blank=0;//表示空格的数目 17 if(len==0) 18 { 19 return NULL; 20 } 21 if(s==NULL) 22 { 23 return NULL; 24 } 25 for(int i=0; i<len; i++) //判断字符串,确定空格的数目,从而确定新的字符串所需的个数 26 { 27 if(s[i]==' ') 28 blank++; 29 } 30 if(blank==0) 31 return s; 32 int new_len=blank*2+len;//计算新的字符数组的长度(本来不该加一,但是最后的输出要求加一) 33 int l=new_len+1; 34 // char *str1 = (char *)malloc(new_len*sizeof(char)+1); 35 char *str1; 36 for(int i=len; i>=0; i--)//算法:有空格就赋值,没有空格直接转换 37 { 38 if(s[i]!=' ') 39 { 40 str1[l-1]=s[i]; 41 l--; 42 } 43 else 44 { 45 str1[l-1]='0'; 46 str1[l-2]='2'; 47 str1[l-3]='%'; 48 l-=3; 49 } 50 } 51 return str1; 52 } 53 int main() 54 { 55 static char str[10000000]; 56 gets(str); 57 char *dest = copystr(str); 58 if(dest != NULL) 59 puts(dest); 60 dest = NULL; 61 return 0; 62 63 }
2、字符串的排列:输入一个字符串,打印出该字符串中字符的所有排列。例如:输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab,cba
答案:递归的过程和这个图差不多。首先我们先固定第一个字符,后面的剩余字符是一部分。第一步我么先把第一个字符和后面的所有字符进行交换,得出第一个字符的所有可能(第一个字符可能出现的情况列出),之后我们在后面的字符部分当做一个字符串重复前面的方法,把第二个字符和后面的所有字符进行交换,得出其他的情况,依次递归重复上面的方法,直到最后一位字符停止。
1 #include<iostream> 2 #include<cstring> 3 #include<stdlib.h> 4 using namespace std; 5 /* 6 解题思路: 7 1.就是将字符串从第一个字符开始,依次和后面的字符进行交换,知道交换到末尾为止 8 ① ② 9 2.比如:abcd--acbd,a,a,a,-- 10 bacd--bcad-- 11 cbad--cabd-- 12 dbca--dcba-- 13 3.但是这个算法是不能有重复的字符的操作 14 */ 15 /* 16 交换函数:将前后两个字符进行交换 17 */ 18 int swap_str(char *str1,char *str2){ 19 char temp=*str1; 20 *str1=*str2; 21 *str2=temp; 22 } 23 /*这个函数是输出排列的函数*/ 24 void permutation(char *str1,char *begin){ 25 if(*begin=='\0')//当递归到末尾的时候,输出该排列 26 cout<<str1<<endl; 27 else{ 28 for(char *ch=begin;*ch!='\0';*ch++){//从子字符串进行递归 29 swap_str(ch,begin); 30 permutation(str1,begin+1); 31 swap_str(ch,begin); 32 } 33 } 34 } 35 void permutation(char *str){ 36 if(str==NULL) 37 return ; 38 permutation(str,str); 39 } 40 41 int main(){ 42 char str[]="abc"; 43 permutation(str,str); 44 return 0; 45 }
3、第一个只出现一次的字符:在字符串中找出第一个只出现一次的字符。如输入“abaccdeff”则输出b
答案:由于题目出现了与字符出现次数相关。我们可以利用一个容器来存放每个字符出现的次数,也就是说这个容器的作用是把一个字符隐射成一个数字。所以我们利用哈希表。
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 using namespace std; 6 char Find_Only_one_char(char *string){ 7 /*①:字符char是一个长度为8的数据类型,一个字节,在32位的操作系统中有256中可能组合。 8 我们创建一个长度为256的数组,每个字母根据其ASSIC码作为数组的下标对应数组的一个数字 9 而数组中存储的是每个字符穿线的次数。 10 ②:复杂度:第一次遍历数组更新一个字符出现次数的时间为O(1),N个就是O(N),第二次遍历读取一个字符 11 的时间是O(1),N个就是O(N),所以总的是O(N)*/ 12 if(string==NULL) 13 return '\0'; 14 const int hashsize=256;//表示256个字符的Assic 15 unsigned int hashtable[hashsize];//哈希容器 16 for(unsigned int i=0;i<hashsize;i++) 17 hashtable[hashsize]=0;//初始化哈希数组 18 19 //开始进行第一次的遍历,如果出现一个字符,则将改字符对应的hash数加1,创建一个字符指针进行遍历 20 char *string1=string; 21 while(*(string1)!='\0'){ 22 //这里string1++:string1最后地址会加1,但是*(string1++)还是当前的地址不会加1,所以a++是后加,++a是先加 23 hashtable[*(string1++)]++;//地址加加,对应的字符的hash数也是加加 24 } 25 //重新让指针指向字符串首部再进行一次遍历 26 string1=string; 27 28 while(*string1!='\0'){ 29 if(hashtable[*string]==1) 30 return *string; 31 string++;//地址加加,继续遍历下一个字符 32 } 33 return '\0'; 34 } 35 int main(){ 36 char string[10000]; 37 while(scanf("%s",string)!=EOF){ 38 cout<<Find_Only_one_char(string)<<endl; 39 } 40 // delete []string; 41 }
4、翻转单词顺序VS左旋转字符串:题目一:输入一个英语句子,翻转句子中单词的顺序,但是单词的字符的顺序不变。变电符号和普通字符一样处理。
答案:这里我们可以考虑一种两次翻转字符串的方法:第一次先是将字符串全部翻转,然后第二次将每个单词翻转,这样就实现了题目的要求。全部翻转比较容易实现就是首尾字符交换就行了,给出两个指针指向首尾,进行交换。
1 #include<iostream> 2 #include<stdlib.h> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 /*这里我们可以通过单词的空格来确定某个单词的起始位置和终止位置。然后在调用change函数就行了,这个方法相当于分治法。 7 先是整体,然后分成每个单词部分翻转。*/ 8 void Change_Oneword_Order(char *begin,char *behind){ 9 if(begin>behind) 10 return ; 11 while(begin<behind){//遍历一个单词,前后顺序调换 12 char temp=*begin; 13 *begin=*behind; 14 *behind=temp; 15 begin++,behind--;//遍历到单词中间位置 16 } 17 18 } 19 char *Change_Onestr_Order(char *str){ 20 if(str==NULL) 21 return '\0'; 22 23 char *behind=str; 24 char *begin=str; 25 while(*behind!='\0') 26 behind++; 27 behind--;//第二指针向前移动一位 28 29 Change_Oneword_Order(begin,behind); 30 31 begin=str;//第二次进行单词的翻转 32 behind=str; 33 34 while(*begin!='\0'){//临界条件:第一指针走到字符串的末尾结束 35 if(*begin==' '){ 36 //找到空格,跳过 37 begin++; 38 behind++; 39 } 40 else if(*behind==' '|| *behind=='\0'){ 41 Change_Oneword_Order(begin,--behind);//从空格前面一个字符开始翻转 42 begin=++behind;//翻转完成回到同一个起跑线 43 } 44 else 45 behind++; 46 } 47 return str; 48 } 49 void print(char *str){ 50 cout<<str; 51 } 52 int main(){ 53 char s[1000]; 54 gets(s); 55 char *ss=Change_Onestr_Order(s); 56 print(ss); 57 }
题目二:字符串的左旋转操作是吧字符串前面的若干个字符转移到字符串的尾部。定义一个函数实现字符串左旋转操作功能。比如输入字符串“abcdef”和2,返回cdefab.
答案:这个题目其实和上面那个差不多,我们可以这样处理:我们先分别把前面后面两部分进行翻转,得到bafedc,然后我们在翻转整个字符串得到cdefab,这样就行了。其实我们也是可以先整体翻转然后在局部翻转,但是在局部反转的时候不好找到翻转位置,实现不如前面方法好。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 void Change_Oneword_Order(char *begin,char *behind){ 6 if(begin>behind) 7 return ; 8 while(begin<behind){//遍历一个单词,前后顺序调换 9 char temp=*begin; 10 *begin=*behind; 11 *behind=temp; 12 begin++,behind--;//遍历到单词中间位置 13 } 14 15 } 16 char *Left_Revolve_Str(char *str,int number){ 17 if(str!=NULL){ 18 int length=static_cast<int>(strlen(str)); 19 20 if(length>0 &&number>0 &&number<length){ 21 //第一部分:0->n 22 char *firststart=str; 23 char *firstend=str+number-1; 24 //第二部分:n->length 25 char *secondstart=str+number; 26 char *secondend=str+length-1; 27 //翻转前半部分 28 Change_Oneword_Order(firststart,firstend); 29 //翻转后半部分 30 Change_Oneword_Order(secondstart,secondend); 31 //翻转整体 32 Change_Oneword_Order(firststart,secondend); 33 } 34 } 35 return str; 36 } 37 void print(char *str){ 38 cout<<str; 39 } 40 int main(){ 41 int number; 42 cin>>number; 43 char s[1000]; 44 cin>>s; 45 char *ss=Left_Revolve_Str(s,number); 46 print(ss); 47 }
5、怎样将整数转换成字符串数,并且不用函数 itoa ?
答案:这个题目看上去十分简单,但是如果仔细考虑很多情况的也不是很简单,比如一些特殊情况:空指针NULL,空字符串,正负号,溢出等方面的测试
详细版本:
1 #include<iostream> 2 using namespace std; 3 long long strToCore(const char *,bool ); 4 enum status{valid=0,invalid=1}; 5 int g_status=valid; 6 7 int strToInt(const char *str){ 8 g_status=invalid; 9 long long num=0; 10 11 if(str!=NULL && *str!='\0'){//不为空也没溢出 12 bool minus=false; 13 if(*str=='+') 14 str++; 15 else if(*str=='-'){ 16 str++; 17 minus=true; 18 } 19 num=strToCore(str,minus); 20 } 21 return num; 22 } 23 long long strToCore(const char *digit,bool minus){ 24 long long num=0; 25 while(*digit!='\0'){ 26 if(*digit>='0' &&*digit<='9'){ 27 int flag=minus?-1:1; 28 num=num*10+flag*(*digit-'0'); 29 //这里没有考虑溢出的情况 30 digit++; 31 } 32 else{ 33 num=0; 34 break; 35 } 36 } 37 if(*digit=='\0') 38 g_status=valid; 39 return num; 40 } 41 int main(){ 42 int num=0; 43 num=strToInt("2345"); 44 cout<<num; 45 }
itao版本:
1 #include <iostream> 2 3 #include <stdlib.h> 4 5 using namespace std; 6 7 8 9 //使用itoa函数 10 11 int main () 12 13 { 14 15 int num=12345; 16 17 char str[7]; 18 19 20 21 itoa(num,str,10); 22 23 cout<<"integer:"<<num<<endl<<"string:"<<str<<endl; 24 25 return 0; 26 27 }
6、已知函数原型是 char *strcpy(char *strDest,const char *strSrc);,其中strDest是目的字符串,strSrc是源字符串。
(1)不调用C++/C的字符串库函数,请编写strcpy函数。
(2)strcpy函数把strSrc的内容复制到strDest,为什么还要char *类型返回值?
答案:
char *strcpy(char *strDest,const char *strSrc) { assert((strDest!=NULL)&&(strSrc!=NULL)); char *address=strDest; while((*strDest++=*strSrc++)!='\0') NULL; return address; }
(2)为了实现链式表达式,返回具体值。
例如:
int length=strlen(strcpy(strDest,"hello world"));
7、下面程序输出是什么?
1 #include<iostream> 2 #include<stdio.h> 3 #include<cstring> 4 using namespace std; 5 int main(){ 6 char s[]="1234567890"; 7 char d[]="123"; 8 strcpy(d,s); 9 cout<<d<<endl<<s<<endl; 10 }
答案:输出结果是1234567890,567890.原因:当初分配的内存地址是连续内存的关系(从后往前存储),原来是:123\01234567890\0,strcpy变成了1234567890\0567890\0,所以在分配空间的时候要给源字符串和目标字符串留有足够的空间。