408数据结构考研笔记——第四章串

6 篇文章 1 订阅
6 篇文章 2 订阅

目录

一、串

1.基础概念

2.存储结构

3.基本操作

二、串的模式匹配

1.朴素模式匹配算法

2.KMP算法

    1.KMP算法

     2.next数组

3.优化的KMP算法

三、课后题目


一、串

1.基础概念

    串本质是字符串,是一种特殊形式的线性表,主要包括串名S、长度n和空串三个概念(空格串 != 空串)。

    子串:主串中任意多个连续的字符组成的字符串,子串在串中的位置 = 子串首个字符在主串中的位置(串的基本操作通常以子串作为操作对象)。

    字符在串中的位置:与位序相同,从1开始计算。

    字符集:类似字符串的定义域,主要包括英文集ASCII码和中文集Unicode。

    编码:函数映射规则f

    y:对应的二进制数(编码结果)

    解码:f-1,反解结果

    乱码:软件对应的解码方式不对。

2.存储结构

    1.定长顺序存储:使用定长数组。

        王道课本中使用的是舍去首个数组空间,再另开辟一块空间存放数组长度的方法,这样的好处是可以让数组下标与字符在串中的位置一一对应(数组从1开始,位置也是从1开始),其次,另外开辟的空间容量由人为规定,可存放较大数据,即数组可以较长(如果在数组第一个空间存放的话,空间仅1B,8位,仅能记录长度小于255的数组)。

#define MaxLen 255
typedef struct{
    char data[MaxLen];
    int length;
}SString;

    2.堆分配存储:使用动态数组,使用后需手动回收。

#define MaxLen 255
typedef struct
{
    char *ch;    //按串长分配存储区,ch指向串的基地址
    int length;
}HString;


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

    3.块链存储:链表存储

        当一个节点仅存放一个元素时,元素占用1B空间,而指针将占用4B空间,导致存储密度较低,因此在使用块链存储时可在一个节点存放多个元素,提高存储密度。

#define MaxLen 255
typedef struct StringNode{
    char ch[4];                  //每个节点存放多个字符,提高存储密度
    struct StringNode * next;
}StringNode, *String;

3.基本操作

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

StrCopy(&T, S):复制操作,将串S复制给串T。

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

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

ClearString(&S):清空操作,但仍保留串S申请的空间。

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

Concat(&T, S1, S2):串连接,将串S1、S2链接成为新的串T。

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数组下标从1开始
    Sub.length = len;
    return true;
}

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

StrCompare(SString S, SString T) 
{
    for(int i=0; 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  retrun i;
    }
    return 0;
}

二、串的模式匹配

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

1.朴素模式匹配算法

   字符串下的朴素模式匹配:暴力解决,利用 Index(S, T) 函数依次比对主串S中每个长为 T.length 的子串,直到找到或全部遍历。

    数组下标下的朴素模式匹配:若当前子串匹配失败,则主串指针 i 指向下一个子串的第一个位置,模式串指针 j 回到模式串的第一个位置,若 j>T.length 则当前子串匹配成功,返回当前子串第一个字符位置 i-T.length。

    最坏时间复杂度:O(mn) = O((n-m+1)m)

int Index(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;
}

2.KMP算法

    1.KMP算法

    原理:每次进行匹配时,可知道失配元素前的子串都与主串对应部分匹配,利用该信息配合模式串的内容特点,在每次失配时无需让主串指针回溯,同时让模式串指针减少回溯范围,可很大程度减少不必要的匹配次数。

    如下图中的情况,在模式串第六个元素不匹配时,由于模式串45位与12位匹配,因此无需将 j 置1 ,可直接让 j=3,这样的对应 j 操作总结成的数组叫next数组(该数组仅与模式串相关,与主串无关,即一个模式串对应唯一固定的next数组)。

    下图模式串的next数组为:

next[0]next[1]next[2]next[3]next[4]next[5]next[6]
011223

    操作:设置一个next数组,每当出现失配情况都去查询对应 j 下的跳转情况。

    最坏时间复杂度:O(m+n) 

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;                       //匹配失败
}

     2.next数组

    作用:当模式串第 j 个字符失配时,从模式串的第next[j]继续往后匹配。

    手算方法

         i next[1] = 0; next[2] = 1; 无脑写,固定的

         ii 其他部分:在不匹配的位置前边,划一条分界线,让模式串一步步往后退,直到分界线前部分完全匹配,写下该情况下分界线后第一个元素对应的下标 j 就是该失配时的next[j]值(如下图 ),若模式串完全跨过分界线则next[j] = 1(全跨过去后 j 指向1)。

3.优化的KMP算法

    本质:优化了next数组为nextval数组。

    原理:在KMP算法原理基础上我们直到的不仅是失配元素前子串的信息,还有当前失配元素“一定不是”哪个元素,因此利用这个我们可以进一步修改next数组,进一步减少匹配次数。

    如下图,原本next数组中next[3] = 1,但因为我们直到主串中 i = 3时一定不是a,因此 S.ch[3]与T.ch[1]一定不匹配,所以可以直接将 j 指针置0,让 i,j 指针直接查询下一元素的情况,无需再浪费时间进行 S.ch[3] 与 T.ch[1] 的匹配了,因此next数组可修改 next[3] = 0

     优化方法:在得出next数组基础上再一遍进行判断修改next数组。

nextval[1] = 0;    //这里将优化后的next数组设为nextval数组
for(int j=2; j<=T.length; j++)
{
    if(T.ch[next[j]] == T.ch[j])
        nextval[j] = nextval[next[j]];    //如果失配元素与next指向元素相同则更新nextval数组
    else
        nextval[j] = next[j];             //如果未出现这种情况直接将next对应赋值给nextval
}

 总结:字符串匹配在考研中并非重点,很少会考大题,因此做好小题的计算和概念认知足矣。

三、课后题目

解答

    1)当模式串中的第一个字符与主串的当前字符比较不相等时,next[1] = 0,表示模式串应后移一位,主串当前指针后移一位,再和模式串的第一个字符进行比较。

    2)当主串的第 i 个字符与模式串中第 j 个字符失配时,主串 i 不回溯,则假定模式串中的第k个字符与主串的第 i 个字符比较,k值应满足条件1<k<j 且'P1,...,Pk-1' = 'Pj-k+1,...,Pj-1',即k为模式串的下次比较位置。k值可能有很多个,为了不使向右移动丢失可能的匹配,右移距离应该取最小,由于 j-k 表示右移的距离,所以取max{k}。k的最大值为 j-1。

    3)除上述两种情况,发生失配时,主串指针 i 不回溯,在最坏情况下,模式串从第1个字符开始与主串的第 i 个字符比较。

2.设有字符串S='aabaabaabaac',P='aabaac' .

    1)求出P的next数组

    2)若S作主串,P作模式串,试给出KMP算法的匹配过程。

解答

    1)012122   (注意next数组除了next[1]不会出现0

    2)看书P119

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值