一些C语言中字符串的算法问题解决实例小结

 更新时间:2020年4月25日 17:35  点击:1201

    字符串问题是面试中经常出现的问题,这类问题有很多,难以不一。下面是几道字符串的题目,网上都能找到解答,自己实现了一下,供网友参考。感觉算法重要的是要有正确的思路,实现起来不是问题。自己一定要多思考,这样收获可能会更多一点。
       
问题1:找两个字符串的最长公共子串。
        具体描述,如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串。注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
    思路:算法书上一般都有介绍,就是用动态规划的思想。关键是要找到最优子结构性质,这一点比较难。另外一个经常问到的问题“求子数组的最大和”,用的也是动态规划的思想。找两个字符串最长公共子串的核心就是找最优子结构。
        设序列X = {x1,x2,…xm}和Y = {y1,y2,…yn}的最长公共子串为Z = {z1,z2,…zk},则
        1 若xm= yn且zk=xm=yn, 则Zk-1是Xm-1和Yn-1的最长公共子串
        2 若xm!=yn且zk!=xm,则Z是Xm-1和Y的最长公共子串
        3 若xm!=yn且zk!=yn,则Z是X和Yn-1的最长公共子串
         其中Xm-1= {x1,x2,…,xm-1},Yn-1 = {y1,y2,…,yn-1}Zk-1 = {z1,z2,…zk-1}
      有了上述关系,则很容易得到递推的式子。用一个二维数组 R 记录结果,其中Z[i][j]表示Xi和Yj的最长公共子串长度。
     (1)如果i = 0或j = 0,Z[i][j] = 0
     (2)如果xi和yj相等,Z[i][j] = Z[i-1][j-1] + 1
     (3) 如果xi和yj不相等,Z[i][j] = max{Z[i-1][j],Z[i][j-1]}
        基本上差不多了,但是题目要求打印最长公共子串,只要用一个数组R记录得到最长公共子串的过程,其中R[i][j]表示Z[i][j]的值是由哪个子问题的解得到的。
       参考代码:

//函数功能 : 打印最长公共子串 
//函数参数 : X为源字符串1,Y为源字符串2,R为记录的过程,row为R的行坐标,col为R的列坐标 
//返回值 :  空 
void LCS_Print(const char *X, const char *Y, int **R, int row, int col) 
{ 
  if(R[row][col] == 1) 
  { 
    LCS_Print(X, Y, R, row-1, col-1); 
    cout<<X[row-1];  //由Xi-1和Yj-1,再加上Xi或Yj得到 
  } 
  else if(R[row][col] == 2) 
    LCS_Print(X, Y, R, row-1, col); //由Xi-1和Yj得到 
  else if(R[row][col] == 3)  
    LCS_Print(X, Y, R, row, col-1); //由Xi和Yj-1得到 
  else 
    return; 
} 
//函数功能 : 求两个字符串的最大公共子串 
//函数参数 : X为源字符串1,Y为源字符串2 
//返回值 :  最大长度 
int LCS(const char *X,const char *Y) 
{ 
  if(X == NULL || Y == NULL) 
    return 0; 
 
  int lenX = strlen(X); 
  int lenY = strlen(Y); 
  if(lenX == 0 || lenY == 0) 
    return 0; 
 
  int i, j; 
  int **C = new int *[lenX+1]; 
  int **R = new int *[lenX+1]; 
 
  //初始化 
  for(i = 0; i <= lenX; i++) 
  { 
    C[i] = new int [lenY+1]; 
    R[i] = new int [lenY+1]; 
    for(j = 0; j <= lenY; j++) 
    { 
      C[i][j] = 0; 
      R[i][j] = 0; 
    } 
  } 
 
  //开始计算  
  for(i = 1; i <=lenX; i++) 
  { 
    for(j = 1; j <=lenY; j++) 
    { 
      if(X[i-1] == Y[j-1]) //字符串的下标从0开始,所以减1,即X1 = X[0] Y1 = Y[0] 
      { 
        C[i][j] = C[i-1][j-1] + 1; 
        R[i][j] = 1;  //由Xi-1和Yj-1,再加上Xi或Yj得到 
      } 
      else 
      { 
        if(C[i-1][j] >= C[i][j-1])  
        { 
          C[i][j] = C[i-1][j]; 
          R[i][j] = 2;   //由Xi-1和Yj得到 
        } 
        else  
        { 
          C[i][j] = C[i][j-1]; 
          R[i][j] = 3;   //由Xi和Yj-1得到 
        } 
      } 
    } 
  } 
  //打印最长公共子串 
  LCS_Print(X, Y, R, lenX, lenY); 
  //记录最大长度 
  int lcs = C[lenX][lenY];   
  //释放空间 
  for(i = 0; i <= lenX; i++) 
  { 
    delete [] C[i]; 
    delete [] R[i]; 
  } 
  delete C; 
  delete R; 
  R = C = NULL; 
  return lcs; 
} 

      
问题2:在字符串中删除特定元素
    具体描述,输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。
    思路:这是字符查找的一个问题,最简单的就是考察第二个字符串的每个字符,然后检查第一个字符串中有没有这个字符,有的话删除。这样的时间复杂度是O(mn)。对于查找,我们容易想到二分查找、散列这些概念。散列的查找是非常快,时间复杂度为O(1)。如果能联想到散列,那么很容易就能给出下面的解法,相信很多人都能想到。需要一个256字节的数组,记录第二个字符串中每个字符的出现情况,0表示未出现,1表示出现。然后遍历第一个字符串,针对每个字符,去查询记录数组,以决定删除与否。
   参考代码:

//函数功能 : 在字符串中删除特定字符 
//函数参数 : pSrc为源字符串,pDel为特定字符集 
//返回值 :  空 
void DeleteSpecialChars(char *pSrc, char *pDel) 
{ 
  if(pSrc == NULL || pDel == NULL) 
    return; 
  int lenSrc = strlen(pSrc); 
  int lenDel = strlen(pDel); 
  if(lenSrc == 0 || lenDel == 0) 
    return; 
  bool hash[256] = { false }; 
  for(int i = 0; i < lenDel; i++) //建立删除字符的索引 
    hash[pDel[i]] = true; 
  //开始删除 
  char *pCur = pSrc; 
  while(*pSrc != '\0') 
  { 
    if(hash[*pSrc] == false) //不用删除 
      *pCur++ = *pSrc; 
    pSrc++; 
  } 
  *pCur = '\0'; 
}

问题3:左旋转字符串,其实也可以左旋数组
   具体描述,定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。
   思路:用了一个小技巧,如果旋转的字符串为XY,其中Y是要旋转到X前面的。只要分别将子字符串 X 和 Y 翻转,然后再将整个字符串再做一次翻转即可。STL的一个算法rotate就是用了这个。
   参考代码:

//函数功能 : 将字符串翻转 
//函数参数 : pBegin指向第一个字符,pEnd指向最后一个字符 
//返回值 :  空 
void ReverseString(char *pBegin, char *pEnd) 
{ 
  if(pBegin == NULL || pEnd == NULL) 
    return; 
 
  while(pBegin < pEnd) 
  { 
    //交换字符 
    char tmp = *pBegin; 
    *pBegin = *pEnd; 
    *pEnd = tmp; 
    //往中间靠拢 
    pBegin++; 
    pEnd--; 
  } 
} 
 
//函数功能 : 将字符串左旋 n 位 
//函数参数 : pSrc为源字符串,n为左旋位数 
//返回值 :  空 
void LeftRotateString(char *pSrc, unsigned n) 
{ 
  if(pSrc == NULL || n == 0 || n > strlen(pSrc)) 
    return; 
 
  int len = strlen(pSrc); 
  ReverseString(pSrc, pSrc + n - 1); 
  ReverseString(pSrc + n, pSrc + len - 1); 
  ReverseString(pSrc, pSrc + len - 1); 
} 

  
问题4:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。
   思路:这一题不难,因为每个字符只有8位,因此可以用计数法。首先统计字符串中每个字符的出现次数,然后从头遍历字符串,对于当前字符,查询统计表,如果出现的次数为1,则输出该字符即可。
   参考代码:

//函数功能 : 在一个字符串中找到第一个只出现一次的字符 
//函数参数 : pStr为源字符串 
//返回值 :  目标字符 
char FirstAppearOnce(char *pStr) 
{ 
  if(pStr == NULL) 
    return '\0'; //未找到返回空字符 
 
  int count[256] = {0}; 
  char *pTmp = pStr; 
   
  while(*pTmp != '\0') //统计字符串中每个字符出现的次数 
  { 
    count[*pTmp]++; 
    pTmp++; 
  } 
  while(*pStr != '\0') //遍历字符串,找到第一个只出现一次的字符 
  { 
    if(count[*pStr] == 1) 
      break; 
    pStr++; 
  } 
  return *pStr; //找到的字符 
} 

      
问题5:写一个函数,它的原形是int continumax(char *outputstr,char *intputstr)。功能:在字符串中找出连续最长的数字串,并把这个串的长度返回,并把这个最长数字串付给其中一个函数参数outputstr所指内存。
        例如:"abcd12345ed125ss123456789"的首地址传给intputstr后,函数将返回9,outputstr所指的值为123456789
    思路:这一题比较简单,扫描一遍字符串即可,遇到数字时将数字个数加1,然后与最长串比较,如果长度大于最长串,更新最长串;遇到非数字时,将数字计数器清零。
    参考代码:函数名和形参名修改了一下,个人习惯。

//函数功能 : 在字符串中找出连续最长的数字串 
//函数参数 : pSrc表示源串,pDest记录最长数字串的起始位置 
//返回值 :  最长数字串的长度 
int ContinueMax(char *pSrc,char * &pDest) 
{ 
  pDest = NULL; 
  if(pSrc == NULL) 
    return 0; 
 
  int max = 0, cnt = 0; 
  while(*pSrc != '\0') 
  { 
    if(*pSrc >= '0' && *pSrc <= '9') //数字 
    { 
      cnt++; 
      if(cnt > max) //更新最长的数字串 
      { 
        pDest = pSrc - cnt + 1; 
        max = cnt; 
      } 
    } 
    else 
      cnt = 0; 
    pSrc++; 
  } 
  if(cnt > max) 
  { 
    pDest = pSrc - cnt + 1; 
    max = cnt; 
  } 
  return max; 
} 

问题6:字符串转换为整数
      问题描述:输入一个表示整数的字符串,把该字符串转换成整数并输出。例如输入字符串"345",则输出整数345。
       思路:转换的过程比较简单,每次读入一个字符,将之前保存的值乘以10,然后再加上这个字符表示的数字。这是正常情况。这个问题主要是考察各种不正常情况的处理。假设函数的声明为 int StrToInt(const char *str);
       (1)输入的字符串指针为空;
       (2)数字前面有正负号的处理;
       (3)字符串表示的数字超过了32位整数能表示的范围,即溢出处理;
       (4)输入了非法字符,即除了数字及正负号的其他字符;
       (5)以字符' 0 '开始的串,' 0 '后面还跟了其他字符,也是非法的。
        如果能很好的处理这些情况,那么程序的健壮性大大增强。其中有两种情况处理起来有点麻烦,第一,如何处理溢出,我们可以使用std::numeric_limits<int>::max(),可以定义一个long long的变量,然后与这个最大值相比,从而判断是否溢出了。第二。由于返回值为一个整型数,那么如果转换失败,返回什么呢?如果是'0 ' ,那么就无法区分正常输入"0"的情况。两种方案,修改函数声明,通过返回值表明转换的成功与否,或者定义一个全局变量,用来保存转换的成功与否。参考代码中使用了第二种方案。
        参考代码:先给出的是std::numeric_limits<int>::max()的用法。

#include <iostream> 
#include <limits>  //需包含这个头文件 
using namespace std; 
int main() { 
  cout << "The maximum value for type float is: " 
    << numeric_limits<float>::max( ) 
    << endl; 
  cout << "The maximum value for type double is: " 
    << numeric_limits<double>::max( ) 
    << endl; 
  cout << "The maximum value for type int is: " 
    << numeric_limits<int>::max( ) 
    << endl; 
  cout << "The maximum value for type short int is: " 
    << numeric_limits<short int>::max( ) 
    << endl; 
} 
      
bool strToIntOK; //全局的变量  
int StrToInt(const char *str)  
{  
  strToIntOK = false;  
  if(str == NULL) //空指针  
    return 0;  
    
  if(str[0] == '0' && str[1] != '\0') //以'0'开始但不是"0" 这条其实可以忽略  
    return 0;  
    
  unsigned i = 0;  
  bool minus = false;  //负数标记  
  if(str[i] == '-' || str[i] == '+') //判断是不是以正负号开始  
  {  
    minus = (str[i] == '-')? true: false;  
    i++;  
  }  
    
  long long result = 0; //转换的结果  
  while(str[i] != '\0')  
  {  
    char c = str[i++];  
    if(c >= '0' && c <='9')  
    {  
      result = result * 10 + (c - '0');  
      if(minus) //负溢出 
      { 
        if(result - 1 > numeric_limits<int>::max())  
          return 0;  
      } 
      else //正溢出 
      { 
        if(result > numeric_limits<int>::max()) 
          return 0;  
      } 
    }  
    else  
    {  
      return 0;  
    }  
  }  
  strToIntOK = true;  
  //结果返回 需强制转换一下  
  return minus? (int)(-result):(int)result;  
}  

[!--infotagslink--]

相关文章

  • C语言实现放烟花的程序

    这篇文章主要为大家详细介绍了C语言实现放烟花的程序,有音乐播放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-23
  • C语言中的字符(char)详细讲解

    本篇文章主要介绍C语言中char的知识,并附有代码实例,以便大家在学习的时候更好的理解,有需要的可以看一下...2020-04-25
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • c#中判断字符串是不是数字或字母的方法

    这篇文章介绍了C#判断字符串是否数字或字母的实例,有需要的朋友可以参考一下...2020-06-25
  • PostgreSQL判断字符串是否包含目标字符串的多种方法

    这篇文章主要介绍了PostgreSQL判断字符串是否包含目标字符串的多种方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-23
  • 详解C++ string常用截取字符串方法

    这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 详解如何将c语言文件打包成exe可执行程序

    这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
  • php字符串按照单词逐个进行反转的方法

    本文实例讲述了php字符串按照单词进行反转的方法。分享给大家供大家参考。具体分析如下:下面的php代码可以将字符串按照单词进行反转输出,实际上是现将字符串按照空格分隔到数组,然后对数组进行反转输出。...2015-03-15
  • MySQL 字符串拆分操作(含分隔符的字符串截取)

    这篇文章主要介绍了MySQL 字符串拆分操作(含分隔符的字符串截取),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-22
  • C#实现字符串转换成字节数组的简单实现方法

    这篇文章主要介绍了C#实现字符串转换成字节数组的简单实现方法,仅一行代码即可搞定,非常简单实用,需要的朋友可以参考下...2020-06-25
  • 使用list stream: 任意对象List拼接字符串

    这篇文章主要介绍了使用list stream:任意对象List拼接字符串操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-09
  • C# 16 进制字符串转 int的方法

    这篇文章主要介绍了C# 16 进制字符串转 int的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • 获取中文字符串的实际长度代码

    JS中默认中文字符长度和其它字符长度计算方法是一样的,但某些情况下我们需要获取中文字符串的实际长度,代码如下: 复制代码 代码如下: function strLength(str) { var realLength = 0, len = str.length, charCode = -1;...2014-06-07
  • PostgreSQL 字符串处理与日期处理操作

    这篇文章主要介绍了PostgreSQL 字符串处理与日期处理操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-01
  • C语言中free函数的使用详解

    free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下...2020-04-25
  • C语言中计算正弦的相关函数总结

    这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下...2020-04-25
  • 详解C语言中的rename()函数和remove()函数的使用方法

    这篇文章主要介绍了详解C语言中的rename()函数和remove()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
  • php 中英文混合字符串截取

    文章介绍一个实用的函数,我们如果用php substr来截取字符在中文上处理的很有问题,今天自己写了一个比较好的中文与英文字符截取的函数,有需要的朋友可以参考下。 ...2016-11-25
  • C语言中求和、计算平均值、方差和标准差的实例

    这篇文章主要介绍了C语言中求和、计算平均值、方差和标准差的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-10
  • C#实现对字符串进行大小写切换的方法

    这篇文章主要介绍了C#实现对字符串进行大小写切换的方法,涉及C#操作字符串的技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25