代码面试之串(转载)

简介: 串的基本概念    串(或字符串),是由零个或多个字符组成的有穷序列。含零个字符的串称为空串,用Ф表示。    串中所含字符的个数称为该串的长度(或串长)。    当且仅当两个串的长度相等并且各个对应位置上的字符都相同时,这两个串才是相等的。

串的基本概念
    串(或字符串),是由零个或多个字符组成的有穷序列。含零个字符的串称为空串,用Ф表示。
    串中所含字符的个数称为该串的长度(或串长)。
    当且仅当两个串的长度相等并且各个对应位置上的字符都相同时,这两个串才是相等的。
    一个串中任意个连续字符组成的子序列(含空串,但不含串本身)称为该串的子串。例如,“a”、“ab”、“abc”和“abcd”等都是“abcde”的子串(平凡子串不包括自身)。

问题: “abcde”有多少个平凡子串?
解:    空串数:1
    含1个字符的子串数:5
    含2个字符的子串数:4
    含3个字符的子串数:3
    含4个字符的子串数:2
共有1+2+3+4+5=15个子串。


串的存储结构

串的顺序存储及其基本操作实现
      串是一种特殊的线性表,在非紧缩格式中,它的每个结点仅由一个字符组成,因此存储串的方法也就是存储线性表的一般方法。存储串最常用的方式是采用顺序存储,即把串的字符顺序地存储在内存一片相邻的空间,这称为顺序串。

串的非紧缩格式(一个单元存放一个字符)与串的紧缩格式(一个单元存放多个字符) ... ... 

顺序存储采用一般顺序表的存储结构,其类型定义如下:

#define MaxSize 100
typedef struct
{
  char data[MaxSize];
  int len;
} strtype;//[紧缩格式]

     其中,ch域用来存储字符串,len域用来存储字符串的当前长度,MaxSize常量表示允许所存储字符串的最大长度。在C语言中每个字符串以'\0'标志结束(在这个结构体中不需要用'\0'标志结束,以下标为0开始存入字符)。

ASCII比较字符串
  由于 "大写字母<小写字母" ,并且a<b,因此在比较时ASCII大的字母比较靠前,但要特殊考虑大小写夹杂的情况

串的链式结构存储
    链式方式存储串,即用单链表形式存储串。这称为链式串或链串。
链串中的结点类型定义:

typedef struct snode
{    char data;
     struct snode *next;
} LiString;


例:在链串中,设计一个算法把最先出现的子串"ab"改为"xyz"。    
解:在串s中找到最先出现的子串"ab",p指向data域值为'a'的结点,其后为data域值为'b'的结点。将它们的data域值分别改为'x'和'z',再创建一个data域值为'y'的结点,将其插入到*p之后。本例算法如下:

复制代码
void Repl(LiString *&s)
{    
    iString *p=s->next,*q;int find=0;
    while (p->next!=NULL && find==0)
    {     
        if (p->data==‘a’ && p->next->data==‘b’)  /*找到*/    
        {   p->data='x';p->next->data='z';        
               /*替换为xyz*/
                q=(lstring *)malloc(sizeof(lstring));
                q->data='y';q->next=p->next;
                p->next=q;
                find=1;
               }
               else p=p->next;
        }
}
复制代码

扩展,编写一个算法实现替换字符串(提示:查找(串的模式匹配,见下面的介绍)、替换相结合)
--------------------------------------------------------

重点:
串的模式匹配————(本人认为较难,很难很难,不过绝对值得一学,学会了有成就感
    设有主串s和子串t,子串t的定位就是要在主串s中找到一个与子串t相等的子串。通常把主串s称为目标串,把子串t称为模式串,因此定位也称作模式匹配。模式匹配成功是指在目标串s中找到一个模式串t;不成功则指目标串s中不存在模式串t。 

算法一:BF算法(Brute-Force)
    Brute-Force简称为BF算法,亦称简单匹配算法,其基本思路是:
    从目标串s="s0s1…sn-1"的第一个字符开始和模式串t="t0t1…tm-1"中的第一个字符比较,若相等,则继续逐个比较后续字符;否则从目标串s的第二个字符开始重新与模式串t的第一个字符进行比较。依次类推,若从模式串s的第i个字符开始,每个字符依次和目标串t中的对应字符相等,则匹配成功,该算法返回i;否则,匹配失败,函数返回-1。 

一种实现:

复制代码
int indexpos(SqString str,SqString substr)
{    int i,j,k,idx=-1;
    for (i=0;i<str.len;i++)
    {   
         for (j=i,k=0;str.data[j]==substr.data[k];j++,k++);
         if (k==substr.len) //注意j每次从i开始,有回溯
                  return(i);
    }
    return(-1);
}
复制代码


二种实现:

复制代码
int index(SqString s,SqString t)
{  
    int i=0,j=0,k;
    while (i<s.len && j<t.len)
    {    if (s.data[i]==t.data[j])      /*继续匹配下一个字符*/
    {     i++; j++;  }   /*主串和子串依次匹配下一个字符*/
    else     /*主串、子串指针回溯重新开始下一次匹配*/
    {     
                  i=i-j+1;     /*主串从下一个位置开始匹配*/
                  j=0;            /*子串从头开始匹配*/
            }
     }
     if (j>=t.len)   k=i-t.len;     /*返回匹配的第一个字符的下标*/
     else  k=-1;                /*模式匹配不成功*/
     return k;
  } 
复制代码


上面这两个算法都不怎么难理解~~
关键是下面这个KMP模式匹配算法:

算法二:
如今呢我也是,似懂非懂,半懂加不懂~~~
当然里面最重要的一步就是求next数组~~~
next数组的功能是,字符串在匹配失效时,主串不用再向前做回溯操作即没有 i=i-j+1; 这步
而只对模式串做右滑操作,到底右滑多少,由next对应模式失配的下标决定。
而相应next数组的产生只取决模式自身,其证明亦很简单
关键是如何得到next数组。。。。

 

串的操作实现代码如下:

//串的顺序存储..
//结构体定义..
#include<stdio.h>
#define MAX 100
typedef struct
{
 char S[MAX];
}SString;
//串的初始化..
void Init_Str(SString &SS)
{
 for(int i=0;i<MAX;i++)
 {
  SS.S[i]='\0';  //把所有值都赋'\0'..
 }
}

//求串的长度..
int Length_Str(SString SS)
{
 int length=0;
 while(SS.S[length]!='\0')  //不是'\0'的话就累加起来,知道最后就知道了长度..
 {
  length++;
 }
 return length;
}

//串的赋值..
void StrEvaluate(SString &SS,char S1[])
{
 for(int i=0;S1[i]!='\0';i++)  //这语句前面可以初始化一下SS.S[]也行..
 {
  SS.S[i]=S1[i];
 }
}

//串的链接..
void StrConcat(SString &SS,SString &S1)
{
 int len1,len2;
 len1=Length_Str(SS);
 len2=Length_Str(S1);
 if(len1+len2+1<MAX)
 {
  for(int i=0;i<len2;i++)
  {
   SS.S[len1+i]=S1.S[i];
  }
 }
 else
 {
  printf("你输入的字符串长度超过了MAX!!!");
 }
}

//求串的字串..
SString SubStr(SString &SS,int i,int length) //SS的第i个位置开始去length长的字串..然后返回..
{
 SString S1;
 Init_Str(S1);
 if(i+length<Length_Str(SS))  //判断字串的长度是否合法..
 {
  for(int j=i;j<=i+length;j++)
  {
   S1.S[j-i]=SS.S[j];
  }
 }
 else
 {
  printf("所取的length超出主串的长度!!!");
 }
 return S1;
}

//串的比较..
int StrComp(SString S1,SString S2)
{
 int i=0;
 while(S1.S[i]==S2.S[i]&&S1.S[i]!='\0')  //第一个元素开始判断是否相等..不相等的话来判断那个大那个小..
 {
  i++;
 }
 if(S1.S[i]==S2.S[i])
 {
  return 0;
 }
 else if(S1.S[i]>S2.S[i])
 {
  return 1;
 }
 else
 {
  return -1;
 }
}

//主函数..
int main()
{
 int k=0,len=0;
 SString SS,S1,S2,S3;
 char ch1[20]={'a','b','c','d','e','f'};
 char ch2[20]={'k','l','n','m','j','k'};
 char ch3,ch4,ch5,ch6,ch7,ch8,ch9;
 char flag=1,flag1=1;
 while(flag)
 {
  printf("**************************************\n");
  printf("*\t1  初始化串\n");
  printf("*\t2  求串的长度\n");
  printf("*\t3  串的赋值\n");
  printf("*\t4  串的链接\n");
  printf("*\t5  求字串\n");
  printf("*\t6  串的比较\n");
  printf("*\t11 推出程序\n");
  printf("**************************************\n");
  scanf("%c",&ch3);
  switch(ch3)
  {
  case '1':
   while(flag1)
   {
   printf("\t---------------\n");
   printf("-\t1. SS初始化\n");
   printf("-\t2. S1初始化\n");
   printf("-\t3. S2初始化\n");
   printf("\t---------------\n");
   scanf("%c",&ch4);
   switch(ch4)
   {
   case '1':
    Init_Str(SS);
    printf("SS初始化成功!");
    break;
   case '2':
    Init_Str(S1);
    printf("S1初始化成功!");
    break;
   case '3':
    Init_Str(S2);
    printf("S2初始化成功!");
    break;
   case '4':
    flag1=0;
    printf("renyijian!!");
          //default:
   } 
        
   getchar();
   }

  case '2':
         printf("\t---------------\n");
   printf("-\t1. 求SS长度\n");
   printf("-\t2. 求S1长度\n");
   printf("-\t3. 求S2长度\n");
   printf("\t---------------\n");
   scanf("%c",&ch5);
   switch(ch5)
   {
   case '1':
    len=Length_Str(SS);
    printf("SS长度=%d",len);
    break;
   case '2':
    len=Length_Str(S1);
    printf("S1长度=%d",len);
    break;
   case '3':
    len=Length_Str(S2);
    printf("S2长度=%d",len);
    break;
   default:
    printf("changdu\n");
   }
  case '3':
   while(flag)
   {
            printf("\t---------------\n");
   printf("-\t1. SS赋值\n");
   printf("-\t2. S1赋值\n");
   printf("-\t3. S2赋值\n");
   printf("\t---------------\n");
   scanf("%c",&ch6);
   switch(ch6)
   {
   case '1':
    StrEvaluate(SS,ch1);
    printf("SS赋值成功\n!");
    break;
   case '2':
    StrEvaluate(S1,ch2);
    printf("S1赋值成功\n!");
    break;
   case '3':
    StrEvaluate(S2,ch1);
    printf("S2赋值成功\n!");
    break;
   //default:
    //printf("fuzhi\n");
   }
   getchar();
   }
  case '4':
            printf("\t---------------\n");
   printf("-\t1. SS和S1链接\n");
   printf("-\t2. SS和S2链接\n");
   printf("-\t3. S1和S2链接\n");
   printf("\t---------------\n");
   scanf("%c",&ch7);
   switch(ch7)
   {
   case '1':
    StrConcat(SS,S1);
    printf("SS和S1链接成功!");
    break;
   case '2':
    StrConcat(SS,S2);
    printf("SS和S2链接成功!");
    break;
   case '3':
    StrConcat(S1,S2);
    printf("S2和S1链接成功!");
    break;
   default:
    printf("lianjie\n");
   }
  case '5':
            printf("\t---------------\n");
   printf("-\t1. SS的字串\n");
   printf("-\t2. S1的子串\n");
   printf("-\t3. S2的子串\n");
   printf("\t---------------\n");
   scanf("%c",&ch8);
   switch(ch8)
   {
   case '1':
    S3=SubStr(SS,3,3);
    break;
   case '2':
    S3=SubStr(S1,3,3);
    break;
   case '3':
    S3=SubStr(S2,3,3);
    break;
   default:
    printf("zichuan\n");
   }
            case '6':
            printf("\t---------------\n");
   printf("-\t1. SS和S1比较\n");
   printf("-\t2. SS和S2比较\n");
   printf("-\t3. S1和S2比较\n");
   printf("\t---------------\n");
   scanf("%c",&ch9);
   switch(ch9)
   {
   case '1':
    k=StrComp(SS,S1);
    printf("%d",k);
    printf("SS和S1比较成功!");
    break;
   case '2':
    k=StrComp(SS,S2);
    printf("%d",k);
    printf("SS和S2比较成功!");
    break;
   case '3':
    k=StrComp(S1,S2);
    printf("%d",k);
    printf("S2和S1比较成功!");
    break;
   default:
    printf("比较\n");
   }
  case '11':
   flag=0;
   printf("任意键结束程序..");
  }
  getchar();
 }
 return 0;
}
相关文章
|
3月前
|
Java 编译器 C++
【Java基础面试一】、为什么Java代码可以实现一次编写、到处运行?
这篇文章解释了Java能够实现“一次编写,到处运行”的原因,主要归功于Java虚拟机(JVM),它能够在不同平台上将Java源代码编译成的字节码转换成对应平台的机器码,实现跨平台运行。
【Java基础面试一】、为什么Java代码可以实现一次编写、到处运行?
|
3月前
|
存储 缓存 Java
面试问Spring循环依赖?今天通过代码调试让你记住
该文章讨论了Spring框架中循环依赖的概念,并通过代码示例帮助读者理解这一概念。
面试问Spring循环依赖?今天通过代码调试让你记住
|
3月前
|
JavaScript 前端开发 程序员
JS小白请看!一招让你的面试成功率大大提高——规范代码
JS小白请看!一招让你的面试成功率大大提高——规范代码
|
6月前
|
缓存 监控 算法
Python性能优化面试:代码级、架构级与系统级优化
【4月更文挑战第19天】本文探讨了Python性能优化面试的重点,包括代码级、架构级和系统级优化。代码级优化涉及时间复杂度、空间复杂度分析,使用内置数据结构和性能分析工具。易错点包括过度优化和滥用全局变量。架构级优化关注异步编程、缓存策略和分布式系统,强调合理利用异步和缓存。系统级优化则涵盖操作系统原理、Python虚拟机优化和服务器调优,需注意监控系统资源和使用编译器加速。面试者应全面理解这些层面,以提高程序性能和面试竞争力。
84 1
Python性能优化面试:代码级、架构级与系统级优化
|
5月前
|
存储 算法 Java
面试高频算法题汇总「图文解析 + 教学视频 + 范例代码」之 二分 + 哈希表 + 堆 + 优先队列 合集
面试高频算法题汇总「图文解析 + 教学视频 + 范例代码」之 二分 + 哈希表 + 堆 + 优先队列 合集
|
6月前
|
数据采集 数据挖掘 Python
最全妙不可言。写出优雅的 Python 代码的七条重要技巧,2024年最新被面试官怼了还有戏吗
最全妙不可言。写出优雅的 Python 代码的七条重要技巧,2024年最新被面试官怼了还有戏吗
|
6月前
|
数据采集 XML 程序员
最新用Python做垃圾分类_python垃圾分类代码用key和format,5年经验Python程序员面试27天
最新用Python做垃圾分类_python垃圾分类代码用key和format,5年经验Python程序员面试27天
最新用Python做垃圾分类_python垃圾分类代码用key和format,5年经验Python程序员面试27天
|
6月前
|
数据采集 机器学习/深度学习 人工智能
最新用python代码画爱心,来自程序猿的浪漫~_python画爱心代码(1),2024年最新面试简历模板免费
最新用python代码画爱心,来自程序猿的浪漫~_python画爱心代码(1),2024年最新面试简历模板免费
最新用python代码画爱心,来自程序猿的浪漫~_python画爱心代码(1),2024年最新面试简历模板免费
|
6月前
|
Python
2024年Python最新刷爆全网的动态条形图,原来5行Python代码就能实现!,2024年最新Python面试必问的HashMap
2024年Python最新刷爆全网的动态条形图,原来5行Python代码就能实现!,2024年最新Python面试必问的HashMap
2024年Python最新刷爆全网的动态条形图,原来5行Python代码就能实现!,2024年最新Python面试必问的HashMap
|
6月前
|
前端开发 JavaScript 编译器
【面试题】 你知道怎么调试线上代码吗?
【面试题】 你知道怎么调试线上代码吗?
下一篇
无影云桌面