数据结构学习——串

本文详细介绍了串(字符串)的基本定义,包括其存储结构(顺序存储和链式存储)以及两种常用的模式匹配算法:BF(暴力破解)和KMP。BF算法的时间复杂度在最坏情况下达到O(mn),而KMP算法则通过利用前后缀信息优化到O(n+m)。文章还提供了C语言实现的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、串的基本定义

串是内容受限的线性表(数据元素只能是字符),是由零个或多个任意字符组成的有限

子串:一个串中任意个连续字符组成的子序列(含空串),另外真子串是指不包含自身的所有子串

主串:包含子串的串

字符位置:字符在序列中的序号

子串位置:子串第一个字符在主串中的位置

空格串:由一个或多个空格组成的串

串相等:两个串长度相等并且各个对应位置的字符都相同

二、串的存储结构

串中元素的逻辑关系与线性表的相同,因此可以采用与线性表相同的存储结构,顺序存储结构的叫顺序串,链式存储结构的叫链串。

顺序存储:元素的逻辑关系直接映射到物理位置,用物理位置的先后表示逻辑关系的先后,最简单的就是用数组实现。

#define MAXLEN 255
typedef struct{
     char ch[MAXLEN+1];   //下标为0的位置不用
     int length;    //当前串的长度
}SString;

链式存储操作方便但是存储密度较低,可将多个字符放在一个结点中克服以上缺点。

#define CHUNKSIZE  80   //块的大小
typedef struct Chunk{
   char ch[CHUNKSIZE];
   struct Chunk *next;
}Chunk;
typedef struct{
   Chunk *head,*tail;   //串的头指针和尾指针
   int curlen;         //串的长度
}LString;

三、串的运算

1.模式匹配算法

目的:确定主串中子串第一次出现的位置(定位)

应用:搜索引擎、语言翻译、拼写检查、数据压缩

种类:BF(暴力破解)、KMP

BF:从主串每一个字符开始依次与子串的字符进行匹配。假设主串S=”aaaaab“,子串T=”aaab“。从S的第一个字符与T的第一个字符开始匹配,一致的话匹配下一个字符,直到第四个字符,不一致,重新从S的第二个字符与T的第一个字符开始匹配,以此类推。

int Index_BF(SString S,SString T){
    int i=1,j=1;
    while(i<=S.length&&j<=T.length){
         if(S.ch[i]==T.ch[j])  {i++;j++;}
         else {i=i-j+2;j=1;}
    }
    if(j>=T.length)  return i-T.length;
    else return 0;
}

//从主串指定位置开始查找的话
int Index_BF(SString S,SString T,int pos){
    int i=pos,j=1;
    while(i<=S.length&&j<=T.length){
         if(S.ch[i]==T.ch[j])  {i++;j++;}
         else {i=i-j+2;j=1;}
    }
    if(j>=T.length)  return i-T.length;
    else return 0;
}

BF算法时间复杂度:设主串长度为n,子串长度为m。最好的情况是比较m次即O(m);最坏情况是m(n-m)+m次即O(mn);平均的话是O(\frac{1}{2}nm)=O(mn)。

KMP算法:利用已经部分匹配的结果而加快子串的滑动速度,且主串的指针不必回溯,可提速到O(n+m)。该算法需要子串存在公共前后缀(前缀从子串第一个字符开始往后扩大,后缀从不匹配位置旁边的字符开始往前扩大,但这个字符是后缀的尾而不是头),以便不匹配时滑动子串,前缀能一下移到后缀的位置。如果有多个前后缀,选最长的,然后移完后前缀有n个字符就从子串第n+1个字符开始比较。

用一个next数组记录下j值(j的含义是当主串某个字符不匹配时,下一个要从子串第j个字符开始匹配)。

定义neweixt[j]函数,当没有公共前后缀时=1,当j为1时=0,当有公共前后缀时=前缀长度+1           

void get_next(SString T,int &next[]){
    i=1;next[1]=0;j=0;         //i指向后缀末尾位置,j指向前缀末尾位置和i之前(包括i,因为前后 
                                 缀可能部分重合)的子串最长公共前后缀的长度
    while(i<T.length){
         if(j==0||T.ch[i]==T.ch[j]){   //前后缀相等j往后移比较下一位,i也后移
              i++;j++;
              next[i]=j;
         }
         else   j=next[j];    //前后缀不相等的情况j回退
    }
}


int Index_KMP(SString S,SString T,int pos){
    i=pos,j=1;
    while(i<S.length&&j<T.length){
       if(j==0||S.ch[i]==T.ch[j]) {i++;j++;}    
       else   j=next[j];
    }
    if  (j>T.length)  return i-T.length;
    else return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值