10.串.

1.串的定义

**串,即字符串(String)**是由零个或多个字符组成的有限序列。一般记为S = ‘a1a2…an’(n>=0)

其中S是串名,单引号括起来的字符序列是串的值;ai可以是字母,数字或其他字符;串中字符的个数n称为串的长度。n=0时的串称为空串

例:S = “HelloWorld!”

T = ‘Hello World!’

  • 有的语言用单引号,有的用双引号。

子串:串中任意个连续的字符组成的子序列。(空串也是子串)

例:‘Hello’,'World!'是串T的子串

主串:包含子串的串。

例:T是子串‘Hello’的主串

字符在主串中的位置:字符在串中的序号。

例:’W‘在T中的位置是7(从1开始,空格也算一个字符)

子串在主串中的位置:子串的第一个字符在主串中的位置。(从1开始,空格也算一个字符)

例:'World!'在T中的位置为7

空串:M=‘’

空格串:N=’ ’

2.串的基本操作

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

在这里插入图片描述

  • Concat(&T,S1,S2):串联接。将S1和S2连接成新串用T来返回结果。

eg:执行操作Concat(&T,S,W)后:T=“iPhone 11 Pro Max?Pro”

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

eg:执行操作SubString(&T,S,4,6)后:T=“one 11”

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

eg:执行操作Index(S,W)后,返回值为11

  • StrCompare(S,T):比较操作(比较的是该字符在计算机中对应的二进数的大小)。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。

在这里插入图片描述

在“abandon”和“aboard”的比较中,先比较了前两个值发现都相同,比较第三个值时a<o,所以字符串“aboard”>“abandon”,这里比较的是“a”和"o"在计算机中存储的值,即ASCII码的大小,“a”为97,“o”为111,故a<o。

3.串的存储结构

3.1串的顺序存储

定义静态数组结构:

#define MAXLEN 255	//预定义最大串长为255
typedef struct{
    char ch[MAXLEN];//每个分量存储一个字符
    int length;		//串的实际长度
}SString;

定义动态数组结构存储串:

typedef struct{
    char *ch;	//按串长分配存储区,指针ch指向串的基地址
    int length;	//串的长度
}HString;
HString S;
S.ch = (char *) malloc(MAXLEN * sizeof(char));//用malloc函数申请一片连续的存储空间,让基地址指针ch指向这篇空间的起始地址(malloc函数申请的空间需要手动释放)
S.length = 0;

存储方案:

在这里插入图片描述

  • 用代码实现求子串操作SubString(&Sub,S,pos,len)
//求子串
bool SubString(SString &Sub, SString S, int pos,int len){
    //判断操作是否越界
    if (pos+len-1 > S.length)
        return false;
	//把选定范围内的字符赋值到数组ch中
    for (int i=pos; i<pos+len; i++)
        Sub.ch[i-pos+1] = S.ch[i];
    Sub.length = len;//把Sub的length值设为len
    return true;
} 
  • 用代码实现比较操作StrCompare(S,T):
//比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0
int StrCompare(SString S, SString T){
    //将两个字符串S和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):
int Index(SString S, SString T){
    int i=1, n=StrLength(S),m=StrLength(T);//f分别求出数组S和T的长度并存入变量n和m中
    SString sub;	//用于暂存子串
    while(i<=n-m+1){//从头到尾依次取出数组S中长度为m的子串
        SubString(sub,S,i,m);//调用取子串操作在数组S中取出长度为m的子串
        if(StrCompare(sub, T)!=0)//调用比较操作比较取出的子串与数组T中的字符串是否相同
            ++i;
        else
            returni;//返回子串在主串中的位置
    }
    return 0//S中不存在与T相等的子串返回0
}

3.2串的链式存储

定义链式存储结构体:

typedef struct StringNode{
    char ch[4];		//每个结点存4个字符
    struct StringNdoe * next;
}StringNode, * String;

在这里插入图片描述

4.串的朴素模式匹配算法

模式串:想尝试在主串中找到的串,未必存在。

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

朴素模式匹配算法(简单模式匹配算法)思想

将主串中与模式串长度相同的子串提出来,挨个与模式串对比,当子串与模式串某个对应字符不匹配时,就立即放弃当前子串,转而检索下一个子串。

在这里插入图片描述

代码实现:

int Index(SString S,SString T){
    int k=1;	//用k来指向当前所对比的子串项的起始位置
    int i=k,j=1;//用i和j分别指向两个子串的对应位置
    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)	//判断j是否超出边界,超出则代表匹配成功
        return K;	//返回当前子串的起始位置
    else
        return 0;
}
  • 若模式串长度为m,主串长度为n,则

匹配成功的最好时间复杂度:O(m)

匹配失败的最好时间复杂度:O(n-m+1) = O(n-m)≈O(n)

  • 若模式串长度为m,主串长度为n,则知道匹配成功/失败最多需要(n-m+1)*m次比较

最坏时间复杂度:O(nm)

5.KMP算法

5.1主要思想

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

KMP算法思路:当子串和模式串不匹配时,主串指针i不回溯,只有模式串指针回溯,模式串指针j=next[j]

对于模式串“google”进行匹配,用i指向主串中的字符,j指向子串中的字符,将它们进行比对,可以发现:

  • 如果j=k时才发现匹配失败,可以说明1~k-1都匹配成功

在这里插入图片描述

根据上图可以得到一下结论:

若当前两个字符匹配,则i++,j++;

若j=1时发生不匹配,则应让i++,而j依然是1;

若j=2时发生不匹配,则应让j回到1;

若j=3时发生不匹配,则应让j回到1;

若j=4时发生不匹配,则应让j回到1;

若j=5时发生不匹配,则应让j回到2;

若j=6时发生不匹配,则应让j回到1;

把以上结论定义成一个数组:

在这里插入图片描述

5.2KMP算法代码

//在主串S中找到模式串T,传入next数组
int Index_KMP(SString S,SString T,int next[]){
    int i=1, j=1;
    while(i<=S.length&&j<=T.elngth){
        if(j==0||S.ch[i]==T.ch[j]){//比较i和j指向的字母是否相等,当第一个字符就不匹配时把j置0
            ++i;
            ++j;	//继续比较后继字符
        }
        else	//当i和j指向的字符不相等时执行以下代码
            j=next[j];	//改变j指向的位置,模式串右移
    }
    if(j>T.length)
        return i-T.length;	//匹配成功
    else
        return 0;
}

算法平均时间复杂度:O(n+m)

5.3关于模式串的next数组

next数组:当模式串的第j个字符匹配失败时,令模式串跳到next[j]再继续匹配,优先选择上下能够匹配的长度较长的情况进行匹配

串的前缀:包含第一个字符,但不包含最后一个字符的子串

串的后缀:包含最后一个字符,且不包含第一个字符的子串

当第j个字符匹配失败时,由前1~j-1个字符组成的串记为S,则:

next[j] = S的最长相等前后缀长度+1,特别的,next[1] = 0,next[2] = 1

例:对于模式串‘ababaa’:

在这里插入图片描述

例:对于模式串‘aaaab’:

在这里插入图片描述

5.4KMP算法的优化

  • 在用了KMP算法后可以发现,在算法运行了仍然进行了一些不必要的比较步骤,可以对KMP算法进一步优化。

  • 可以根据next[j]数组求出一个新的数组nextval[j],该新数组对之前的多余步骤进行了优化,减少了不必要的比较操作。

例:对于模式串‘aaaab’:

在这里插入图片描述

  • KMP算法优化:当子串和模式串不匹配时j=nextval[j],此时next[j]数组被完全替代;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值