字符串面试考法总结

文章转自:http://blog.csdn.net/zhangmuyan/article/details/8721257

最近一直在研究字符串的问题,有关c的,也有c++string类的。看到这篇文章,正是我自己在学习的内容就转载了。


一般面试字符串的题目分四种:1, 基本运算(求长度,连接,比较)2. 格式转换(atoi, itoa) 3.字符串翻转 4. 模式匹配。

1. 基本运算

a. 赋值操作

函数原型:int StrAssign(const char *s, char *t)

函数说明:将s的内容付给t

函数定义:

int StrAssign(const char *s, char *t){
 char *p = t;
  while(*s !='\0'){
  *p++ = *s++;
 }
 *p = '\0';

 return 0;
}

b. 连接操作

函数原型:int Concat(char *s, const char *t)

函数说明:将串t连接到串s的尾部,形成一个新串

函数定义:
int Concat(char *s, const char *t){
 //首先找到串s的尾部
 char *p = s;

 while(*p != '\0') p++;//循环结束以后的p就是s的尾部

  while(*t !='\0')   *p++ = *t++; 
  *p='\0';//添加尾部字符

  return 0;
}

c.求长度

函数原型:int StrLen(char *s)

函数说明:返回串s的长度

函数定义:

int StrLen(char *s){
 int len = 0;

 while(*s != '\0'){
  len ++;
  s++;
 }

 return len;
}

d. 字符串比较

函数原型:int StrCmp(const char *s, const char *t)

函数说明:比较两个串的大小,返回值为-1,0,1表示s<t, s=t,s>t。

函数定义:

int StrCmp(const char *s, const char *t){
 while((*s != '\0')&&(*t != '\0')){
   if(*s++ - *t++ > 0) return 1;
  else if( *s++ - *t++ <0) return -1;
  else return 0;
 }

 return 0;
}

//不区分大小写的字符串比较函数
int stricmp(const char *s, const char *t){ 
 while((*s != '\0')&&(*t != '\0')){  
  if(toupper(*s++) - toupper(*t++) > 0) return 1;
  else if(toupper(*s++) - toupper(*t++) <0) return -1;
  else return 0;
 }

 return 0;
}

2. 整数与字符串之间的相互转换

 

 

a.将字符串转换成整数

 

 

      例如有一字符串str[]=“1234”,最直观的转换方法就是依次将每一个字符转换成相应的数字,然后乘10累加,即((1*10+2)*10+3)*10+4。想要得到每一位相应的数字,只需用每个字符减去数字0的ASCII码,即2= str[1]-'0'。
      如果字符串首字符标示的是一个负数,即str[0]=='-'。那么此时应该设置一个符号判断标示位(用来判断以后得到的整数是否需要乘-1),并把要处理的字符数组下标设置为1。
      int str_to_int(char str[])
      {
        int i=0, isNeg=0,num=0;
        if(str[0]=='-')
          {isNeg=1;
           i=1;
          }
        while(str[i])
          {num=num*10+(str[i]-'0');
           i++;
          }
        if(isNeg)
          num*=-1;
        return num;
      }


b.将整数转换为字符串
      将整数转换为字符串相对要复杂些。通过取模,并结合商与余数的关系,可以有几种方法。例如,123除以100,商为1,余数为23,这样,首先可以把“1”保存到字符串里。然后23除以10,商为2,余数为3。这样就可以得到字符串“123”了。但是,怎样来确定整数的位数确实比较麻烦。有一种相对简单的方法。
      首先将123除10取模,得到数字3,此时商为12。再将12除10取模,得到数字2,商为1。这样,我们可以得到字符串“321”,接着进行一次逆序即可得到想要的字符串。
    同样,如果该整数小于0,我们需要人为地将其变成正数,再进行取模运算!!
       char int_to_str(int num,char str[])
       {
        int i=0,j=0,isneg=0;
        char temp[10];
        if(num<0)
          {
           num*=-1;
           isNeg=1;
          }
        do{
           temp[i]=(num)+'0';
           num/=10;
           i++;
            }while(num)    //使用do-while结构保证当num为0时也执行循环。
        if(isNeg)
          temp[i++]='-';
        while(i>0)
          str[j++]=temp[--i];
        str[j]='\0';      //字符串结束符不能忘掉。
       }


 

3、字符串翻转,这里有两种翻转,第一种是翻转整个字符串,第二种是翻转句子中的单词。

a. 翻转整个字符串

函数原型:void Reverse(char *s1)

函数说明:"abcd"=>"dcba"

函数定义:

void Reverse(char *s1){

 char *p = s1; 
 while(*p!='\0'){
  p++;
 }
 char ch_t;
 p--;
 char *p1 = s1;
 while(p1 < p){
  ch_t = *p1;
  *p1 = *p;
  *p = ch_t; 
  p--;
  p1++;  
 }

 return;
}

b. 翻转句子中的单词

函数原型:void ReverseWord(char *s1)

函数说明:"today is sunday"=>"sunday is today"

函数定义:

void ReverseWord(char *c){
//先对整个句子进行翻转
 char *p = c;
 char *p1;
 int count = 0;  
//在对句子的每个单词进行翻转
 while(*p!='\0'){
  p++;
  count++;
  if((*p == ' ')||(*p =='\0')){
   //记录下这个位置
   p1 = p - count;
   if(*p == ' ') p++;
   ReverseChar(p1, 0, count-1);   
   count = 0;
  }
 }
Reverse(c);
}

在ReverseWord函数调用了ReverseChar函数对给出字符串的起始位置做翻转

void ReverseChar(char *ch,  int start, int end){
 char *p1 = ch;
 char *p = ch;
 char ch_t;

 while(start < end){
  ch_t = *(p1+start);
  *(p1+start) = *(p + end);
  *(p + end) = ch_t; 
  end --;
  start++;  
 }
}


 

4、字符串的模式匹配

给一个串T,和子串P,返回是否子串p是否和串T中有子串匹配,如果有,返回位置;没有返回0。

假设P为给定的子串,T是待查找的字符串

T:   t0      t1     t2      t3 .... tm-1 ... tn-1
P:   p0      p1     p2      p3 .....pm-1      

P中的字符依次与T中的字符进行比较,遇到不相等的字符,则可将P右移一个字符,从新进行比较,直到某次匹配成功或者到达P的最右字符移出T为止。

: 若P="aaaba", T="aaabbaaaba", 则匹配过程如下图
 T:     a   a   a   b   b   a   a   a   b  a
 P:     a   a   a   b   a                                                                 

            a   a   a   b   a                 
                                .....
                            a   a   a   b  a            
上述朴素字符串匹配算法的时间复杂性为0(M*N).

朴素匹配算法函数定义:
int Index(const char *res, const char *str, int position)
{

 int i=0;
 int j=0;
 int lengthRes=strlen(res);
 int lengthStr=strlen(str);
 while(i<=lengthRes && j<=lengthStr)
 {
  if(res[i]==str[j])
  {
   ++i; 
   ++j;
  }
  else if(j == lengthStr)
   return i-lengthStr;
  else
  {
   //如果不匹配, j从头开始重新匹配

   i=i-j+1;
   j=0;
  }
 }
 return -1;
}

在朴素的匹配算法中,从头回溯重新匹配是没有必要的。

KMP主要解决两个问题:
a. 当字符串比较出现不等时,确定下一趟比较前应该将P右移多少个字符; 
b. P右移后,应该从哪个字符开始和T中刚才比较时不等的那个字符继续开始比较.

每当一趟匹配过程中出现字符比较不等时,不需回溯主串S的指针,而是利用已经得到的“部分匹配”结果将模式串向右“滑动”尽可能远的一段距离后,继续进行比较。模式串到底向右滑动多少,在KMP算法中是用一个数组来存储的。针对模式串中的每个索引j,都将有一个对应的值。此值的含义为模式串中位置从0到j-1构成的串中所出现的首尾相同的子串的最大长度加1。

因此,KMP算法计算next数组是关键。

void GetNext(char *p, int next[]){
 int i,j, slen;
 slen = strlen(p);
 i = 0;
 next[0] = -1;
 j = -1;
 while(i<slen){
  if((j==-1)||(p[i] == p[j])){
   ++i;
   ++j;
   next[i] = j;
  }
  else{
   j = next[j];
  }
 }
}

KMP匹配算法函数:
int Index_KMP(char *s, char *p, int pos, int next[]){
 int i,j, slen, plen;
 i = pos -1;
 j =-1;
 slen = strlen(s);
 plen = strlen(p);
 while((i<slen)&&(j<plen)){
  if((j == -1)||(s[i] == p[j])){
   ++i;
   ++j;
  }
  else{
   j = next[j];
  }
 }
  if(j>=plen) return i - plen;
  else return -1;
 }


你可能感兴趣的:(字符串面试考法总结)