【数据结构】第4章 串

这是本人根据王道考研数据结构课程整理的笔记,希望对您有帮助。

4.1 串

4.1.1 串的定义和基本操作

串的定义

,即字符串(String)是由零个或多个字符组成的有限序列。一般记为: S = ′ a 1 a 2 … a n ′ ( n ≥ 0 ) S='a_1a_2\dots a_n'(n\ge0) S=a1a2an(n0)(C、Java用双引号,Python用单引号)。

  • S S S串名,单括号括起来的字符序列是串的值
  • a i a_i ai可以是字母、数字或其他字符
  • 串中字符的个数 n n n称为串的长度
  • n = 0 n=0 n=0时的串称为空串
  • 子串:串中任意个连续的字符组成的子序列
  • 主串:包含子串的串
  • 字符在主串中的位置:字符在串中的序号(从1开始)
  • 子串在主串中的位置:子串的第一个字符在主串中的位置

串和线性表

串是一种特殊的线性表,数据元素之间呈线性关系

串的数据对象限定为字符集(如中文字符、英文字符、数字字符、标点字符等)

串的基本操作,如增删改查等通常以字串为操作对象


串的基本操作

假设有串T=""S="iPhone 11 Pro Max"W="Pro"

StrAssign(&T, chars)赋值操作。把串T赋值为chars。

StrCopy(&T, S)复制操作。由串S复制得到串T。

StrEmpty(S)判空操作。若S为空串,则返回true,否则返回false。

StrLength(S)求串长。返回串S的元素个数。

ClearString(&S)清空操作。将S清为空串。

DestroyString(&S)销毁串。将串S销毁(回收存储空间)。

Concat(&T, S1, S2):串联接。用T返回由S1和S2联接而成的新串。

SubString(&Sub, S, pos, len):求子串。用Sub返回串S的第pos个字符起长度为len的字串。

Index(S, T)定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。

StrCompare(S, T)比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S=T,则返回值=0;若S<T,则返回值<0。
在这里插入图片描述

  • 英文字符——ASCII码
  • 中英文字符——Unicode字符集
    • 对于同一个字符集,可以有多种编码方案,如:UTF-8,UTF-16。

总结:
在这里插入图片描述

4.1.2 串的存储结构

串的顺序存储

//【静态数组】定长顺序存储
#define MAXLEN 255
typedef struct
{
    char ch[MAXLEN];
    int length;
}SString;
//【动态数组】堆分配存储
typedef struct
{
    char *ch;
    int length;
}HString;

HString S;
S.ch = (char *)malloc(MAXLEN * sizeof(char));
S.length = 0;

串的链式存储
在这里插入图片描述

SubString(&Sub, S, pos, len):求子串。用Sub返回串S的第pos个字符起长度为len的字串。

bool SubString(SString &Sub, SString S, int pos, int len)
{
    //子串范围越界
    if(pos+len-1 > S.length)
        return false;
    for(int i = pos; i < pos + len; i++)
        Sub.ch[i-pos+1] = S.ch[i];
    Sub.length = len;
    return true;
}

StrCompare(S, T)比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S=T,则返回值=0;若S<T,则返回值<0。

int StrCompare(SString S, SString T)
{
    for(int i=1; i<=S.length && i<=T.length; i++)
    {
        if(S.ch[i] != T.ch[i])
            return S.ch[i]-T.ch[i];
    }
    //扫描过的所有字符都相同,则长度长的串更大
    return S.length-T.length;
}

Index(S, T)定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。

int Index(SString S, SString T)
{
    int i = 1, n = StrLength(S), m = StrLength(T);
    SString sub;	//用于暂存子串
    while(i <= n-m+1)
    {
        SubString(sub, S, i, m);
        if(StrCompare(sub, T) != 0)
            ++i;
        else return i;	//返回子串再主串中的位置
    }
    return 0;	//S中不存在于T相等的子串
}

总结:
在这里插入图片描述

4.2 串的模式匹配

4.2.1 串的朴素模式匹配算法

在这里插入图片描述

串的模式匹配:再主串中找到与模式串相同的子串,并返回其所在位置。


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

在这里插入图片描述

4.2.2 KMP算法

朴素模式匹配算法的缺点:当某些子串与模式串能部分匹配时,主串的扫描指针i经常回溯,导致时间开销增加。
在这里插入图片描述

求next数组的方法
在这里插入图片描述

串的前缀:包含第一个字符,且不包含最后一个字符的子串合集。(如果是单字符就不是前缀)

串的后缀:包含最后一个字符,且不包含最后一个字符的子串合集。(如果是单字符就不是前缀)

模式串取前缀(从前往后取),主串取后缀(从后往前取),看看前缀和后缀是否相等。最长相等前后缀的长度+1即为next[j]的值。

注:

  1. 结论:next[1] = 0
  2. 结论:next[2] = 1
void get_next(SString T, int next[])
{
    int j = 1; k = 0;
    next[1] = 0;
    while(j < T.length)
    {
        if(k == 0 || T.ch[j] == T.ch[k])
        {
            ++j;
            ++k;
            //若pj = pk,则next[j+1] = next[j] + 1
            next[j] = k;
        }
        else
            //否则令k = next[k],循环继续
            k = next[k];
    }
}

代码解释:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ly5izgEe-1650375154062)(数据结构.assets\051048038058339.png)]

假设第j位以及第j位之前next数组的都填完了,那么就会有如下已知条件:

  • next[j] == kA1子串 == A2子串
  • next[k] == 绿色色块所在的索引B1子串 == B2子串
  • next[绿色色块所在的索引] == 黄色色块所在的索引C1子串 == C2子串

由上面的结论可以衍生出:

  • A1子串 == A2子串 + B1子串 == B2子串B1子串 == B2子串 == B3子串
  • B1子串 == B2子串 == B3子串 + C1子串 == C2子串C1子串 == C2子串 == C3子串 == C4子串

现在考虑第j+1位的情况:

  1. 如果str[j] == str[k],那么很明显next[j+1] == k+1(比较A1子串和A2子串延长后是否仍然一致)
  2. 如果str[j] != str[k],那么令k == next[k](比较B1子串和B3子串延长后是否仍然一致),继续上一步操作直至str[j] == str[k]

KMP算法

int Index_KMP(SString S, SString T, int next[])
{
    int i = 1, 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;
}

KMP算法优化
在这里插入图片描述

在匹配j=4时,匹配失败会跳到j=1,但是此时j=1对应的模式串也是g,这一次的对比是毫无意义的。

由此引出了nextval数组:
在这里插入图片描述

nextval数组的求法:

//【先算出next数组】
//【令nextval[1] = 0】
for(int j = 2; j<=T.length; j++)
{
    if(T.ch[next[j]] == T.ch[j])
        nextval[j] = nextval[next[j]];
    else
        nextval[j] = next[j];
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quentin_HIT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值