数据结构与算法·第4章【串】

串是有限长的字符序列,由一对单引号相括,如: “a string”
可以理解为c++的 s t r i n g string string

基本操作

在这里插入图片描述
在这里插入图片描述
S t r A s s i g n , S t r C o m p a r e , S t r L e n g t h , C o n c a t , S u b S t r i n g StrAssign,StrCompare,StrLength,Concat,SubString StrAssign,StrCompare,StrLength,Concat,SubString是基本操作集

定长顺序存储表示

  #define  MAXSTRLEN  255  // 用户可在255以内定义最大串长
  typedef unsigned char Sstring[MAXSTRLEN + 1];   // 0号单元存放串的长度

就是用了char数组模拟string,只不过在数组首位放了数组长度
串的实际长度可在这个予定义长度的范围内随意设定,超过予定义长度的串值则被舍去,称之为“截断”

基本操作

Status SubString(SString &Sub, SString S, int pos, int len) {
    
    
    // 用Sub返回串S的第pos个字符起长度为len的子串。
    //其中,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-//pos+1 。
    if ( pos<1 ||  pos>S[0] || len<0 || len>S[0]-pos+1 )
             return ERROR;
    Sub[1..len] = S[pos..pos+len-1];
    Sub[0] = len;           return OK;
}//SubString

注意S[0]-pos+1这里需要+1

堆分配存储表示

typedef struct {
    
    
   char *ch;      // 若是非空串,则按串长分配存储区,否则ch为NULL
   int  length;   // 串长度
} HString;

通常,C语言中提供的串类型就是以这种存储方式实现的。系统利用函数malloc( )和free( )进行串值空间的动态管理,为每一个新产生的串分配一个存储区,称串值共享的存储空间为“堆”。C语言中的串以一个空字符为结束符, 串长是一个隐含值。简而言之:通过malloc(),free()为串动态分配存储空间

基本操作

Status StrInsert(HString &S, int pos, HString T) {
    
    
      // 1≤pos≤StrLength(S)+1。
       //在串S的第pos个字符之前插入T
      if (pos<1 || pos>S.length+1 ) return ERROR; 
      // pos不合法
      if (T.length) {
    
    
      //T非空,则重新分配空间,插入T 
      if (!(S.ch = (char *) realloc ( S.ch, ( S.length+T.length ) * sizeof ( char ))))
          exit (OVERFLOW);
      for ( i=S.length-1;i>=pos-1; --i)
      //为插入T而腾出位置
      S.ch[i+T.length] = S.ch[i];
      S.ch[pos-1..pos+T.length-2] = T.ch[0..T.length-1]//插入T
      S.Length += T.length;
   }
   return OK;
} // StrInsert

注意这一步中的S.ch[pos-1..pos+T.length-2],感觉这些细节其实不那么能注意到
这里i=S.length-1,是因为:pos给出的视角是数组从1开始到 l e n g t h length length的存储 [ 1 ] … … [ l e n g t h ] [1]……[length] [1]……[length],而实际上堆分配的串的存储应该是从0开始的 [ 0 ] … … [ l e n g t h − 1 ] [0]……[length-1] [0]……[length1]

r e a l l o c ( ) realloc() realloc()

  • 如果 size 的值为 0,则等价于调用 free(ptr),即释放 ptr 所指向的内存空间。
  • 如果传入的 ptr 不为 NULL,并且 size 大于原先分配的内存大小,则尝试将之前分配的内存空间扩展为 size 字节的大小。如果当前内存不足以扩展到 size 字节,则重新分配一块新的内存空间,并将原有空间中的数据复制到新的内存空间上,最后释放原有的内存空间,返回新的内存空间的地址。
  • 如果传入的 ptr 不为 NULL,并且 size 小于或等于原先分配的内存大小,则尝试缩小原有的内存空间到指定的大小。如果原有的内存空间过大,多余的部分会被返回给内存管理系统。
Status SubString(HString& Sub, HString S, int pos, int len) {
    
    
    // 用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串
    if (pos < 1 || pos > S.length || len < 0 || len > S.length - pos + 1) return ERROR;
    if (Sub.ch) free(Sub.ch); // 释放旧空间
    if (!len) {
    
    
        Sub.ch = NULL;
        Sub.length = 0; // 空子串
    } else {
    
    
        Sub.ch = (char*)malloc(len * sizeof(char)); // 完整子串
        for (int i = 0; i < len; i++) Sub.ch[i] = S.ch[pos + i - 1];
        Sub.length = len;
    }
    return OK;
} // SubString

仍然要强调的是,注意特殊情况进行特殊判断

串的块链存储表示

链表来存储串值,由于串的数据元素是一个字符,因此用链表存储时,通常一个结点中可以存放一个字符,也可以存放多个字符。

在这里插入图片描述

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

实际应用时,可以根据问题所需来设置结点的大小。
例如: 在编辑系统中,整个文本编辑区可以看成是一个串,每一行是一个子串,构成一个结点。即: 同一行的串用定长结构(80个字符), 行和行之间用指针相联接。

串的模式匹配算法

朴素模式匹配

int Index(SString S, SString T, int pos) {
    
    
    // 返回子串 T 在主串 S 中第 pos 个字符之后的位置。若不存在,
    // 则函数值为 0。其中,T 非空,1 <= pos <= StrLength(S)。
    i = pos;
    j = 1;
    while (i <= S[0] && j <= T[0]) {
    
     // 0 下标存储字符串长度
        if (S[i] == T[j]) {
    
     
            ++i;  
            ++j; // 继续比较后继字符
        } else {
    
     
            i = i - j + 2;   
            j = 1; // 指针后退重新开始匹配
        }
    }
    if (j > T[0]) return i - T[0];
    else return 0;
} // Index

关于 i = i − j + 2 i = i - j + 2 i=ij+2 的含义:当在匹配中出现了不匹配的情况时,我们需要将指针 i i i j j j 同时后移一位再进行匹配,以尝试匹配剩下的部分。由于当前指针 i 已经匹配过了前 j − 1 j - 1 j1个字符,因此需要将 i 前移 j − 1 - 1 1 位,再加上 2 2 2,即可保证 i i i恰好后移一位,准备与 j j j 再次匹配。

这种位置类的细节,完全值得好好注意虽然暂时还不知道考试会不会扣这方面的细节,但是自己写代码的时候,也需要避免越界访问

时间复杂度为:O(m*n)

KMP算法

int Index_KMP(SString S, SString T, int pos) {
    
    
    // 利用模式串 T 的 next 函数求 T 在主串 S 中第 pos 个字符之后的位置的 KMP 算法。
    // 其中,T 非空,1 ≤ pos ≤ StrLength(S)
    int next[MAXSIZE];
    GetNext(T, next);
    int i = pos;
    int j = 1;
    while (i <= S[0] && j <= T[0]) {
    
     // 0 下标存储字符串长度
        if (j == 0 || S[i] == T[j]) {
    
     
            ++i;  
            ++j; // 继续比较后继字符
        } else {
    
    
            j = next[j]; // 模式串向右移动
        }
    }
    if (j > T[0]) {
    
    
        return i - T[0] + 1; // 匹配成功
    } else {
    
    
        return 0;
    }
} // Index_KMP

这个算法稍微有点复杂
讲解
其中,考试 n e x t next next数组要自己弄出来

void GetNext(SString T, int* next) {
    
    
    int i = 1;
    int j = 0;
    next[1] = 0;

    while (i < T[0]) {
    
    
        if (j == 0 || T[i] == T[j]) {
    
    
            ++i;
            ++j;
            next[i] = j;
        } else {
    
    
            j = next[j];
        }
    }
}

习题

KMP匹配

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_61786525/article/details/130934784
今日推荐