数据结构笔记 | 第五章 | 串

Chapter 5 串

  • 是什么:由零个或多个字符组成的有限序列,又叫字符串。
  • 串中的字符数目n称为串的长度。零个字符的串称为空串
  • 空格串是只包含空格的串。注意它与空串的区别,有内容有长度,而且可以不止一个空格。
  • 子串:串中任意个数的连续字符组成的子序列称为该串的子串,包含子串的串称为主串。子串在主串中的位置就是子串的第一个字符在主串中的序号。
  • 串的比较是通过组成串的字符之间的编码来进行的。计算机中的常用字符是使用标准的ASCII编码,用7位二进制数表示一个字符,总共可表示128个字符。后来发现不够用,于是扩展ASCII码由8位二进制数表示一个字符,总共可以表示256个字符。其他语言用Unicode编码,比较常用的是由16位的二进制数表示一个字符,约是6.5万多个字符。为了和ASCII码建通,Unicode的前256个字符与ASCII码完全相同。

串的抽象数据类型

  • 基本操作上,线性表更关注的是单个元素的操作,比如查找、删除、插入一个元素,但串中更多的是查找字串位置、得到指定位置子串、替换子串等操作。
ADT 串(string)
Data 
    串中元素仅由一个字符组成,相邻元素具有前驱和后继关系。
Operation 
    StrAssign(T,*chars): 生成一个其值等于字符串常量chars的串T。
    StrCopy(T,S): 串S存在,由串S复制得串T。
    ClearString(S): 串S存在,将串清空。
    StringEmpty(S): 若串S为空,返回true,否则返回false
    StrLength(S):  返回串S的元素个数,即串的长度。
    StrCompare(S,T):  若S>T,返回值>0,若S=T,返回0,若S<T,返回值<0Concat(T,S1,S2): 用T返回由S1和S2连接而成的新串。
    SubString(Sub,S,pos,len): 串S存在,1<=pos<=StrLength(S),0<=len<=StrLength(S)-pos+1,
                              用Sub返回串S的第pos个字符其长度为len的子串。
    Index(S,T,pos):串S和T存在,T是非空串,1<=pos<=StrLength(S)。
                   若珠串S中存在和串T值相同的元素则返回它在珠串S中
                   第pos个字符之后第一次出现的位置,否则返回0Replace(S,T,V): 串S、T和V存在,T是非空串。用V替换主串S中出现的
                    所有与T相等的不重叠子串。
    StrInsert(S,pos,T): 串S和T存在,1<=pos<=StrLength(S)+1,在串S的第pos个字符之前插入串T。
    StrDelete(S,pos,len): 串S存在,1<=pos<=StrLength(S)-len+1。
                         从串S中删除第pos个字符起长度为len的子串。
endADT
  • 实现Index操作
/*T为非空串。若主串S中第pos个字符之后存在与T相等的子串*/
/*则返回第一个这样的子串在S中的位置,否则返回0*/
int Index(String S, String T, int pos)
{
    int n,m,i;
    String sub;
    if(pos>0)
    {
        n=StrLength(S);
        m=StrLength(T);
        i=pos;
        while(i<=n-m+1)
        {
            SubString(sub,S,i,m);
            if(StrCompare(sub,T)!=0)
                ++i;
            else 
                return i;
        }
    }
    return 0;
}

串的存储结构

  • 串的顺序存储结构:用一组地址来纳许的存储单元来存储串中的字符序列。一般用定长数组来定义,在后面加"\0"表示串的终结。
  • 串的链式存储结构:与线性表类似,但如果一个结点放一个字符会存在很大浪费,因此一个结点可放多个字符,最后一个结点若是未被沾满,可用"#"或其他非串值字符补全。

朴素的模式匹配算法

  • 串的定位操作通常称作串的模式匹配。
/*朴素的模式匹配,时间复杂度O((n-m+1)*m)。
  返回子串T在主串S中第pos个字符之后的位置。若不存在,则返回0
  T非空,1<=pos<=StrLength(S)
  假设主串S和要匹配的子串T的长度存在S[0]与T[0]中
  实现:从主串 S 的第 pos 个字符起和模式 T 的第一个字符比较之,
       若相同,则两者顺次的去比较后续的每一个字符,
       否则从主串 S 的下一个字符起再重新和模式 T 的字符比较之。
*/
int Index(String S, String T, int pos)
{
    int i=pos;
    int j=1;
    while(i<=S[0] && j<=T[0])
    {
        if(S[i]==T[j]) /*两字母相等则继续*/
        {
            ++i;
            ++j;
        }
        else
        {
            i=i-j+2; /*退回到上次匹配首位的下一位。ij 走的长度是一样的,
                       如果从0开始,那么相减之后,故+1到下一位,如果是从1开始存,那么+2到下一位。*/
            j=1;
        }
        if(j>T[0]) /*若匹配完,肯定是子串先比完*/
            return i-T[0];
        else 
            return 0;
    }
}

KMP模式匹配算法

  • 原理:i值不回溯。T串的首字符与后面字符比较,如果发现有相等字符,j值的变化就会不相同。也就是说,j值的变化与主串其实没什么关系,关键取决于T串的结构中是否有重复的问题。把T串各个位置的j值的变化定义为next数组,next数组的长度就是T串的长度。
    定义(j从1开始):
    在这里插入图片描述
  • next数组值推导:
    方法①:next[1]=0;next[2]=1;从3开始,计算next[j]看1到 j-1的子串,next[j]=子串前缀与后缀相同字符个数+1(没有相同的时候就为1);
    方法②:得到T[j-1]的字符,不断顺着next[]往前找,找到第一个相等的T[i],next[j]=next[i递归的上一个]+1;若找到下标为 1 还不等,直接赋值 next[j] = 1。
  • 算法实现:
/*计算next数组*/
void get_next(String T,int *next)
{
    itn i,j;
    i=1;
    j=0;
    next[1]=0;
    while(i<T[0])
    {
        if(j==0 || T[i]==T[j]) /*T[i]表示后缀的单个字符,
                                 T[j]表示前缀的单个字符*/
        {
            ++i;
            ++j;
            next[i]=j;
        }
        else
            j=next[j]; /*若字符不相同,则j值回溯*/
    }
}

int Index_KMP(String S, String T,int pos)
{
    int i=pos;
    int j=1;
    int next[255];
    get_next(T,next);
    while(i<=S[0] && j<=T[0])
    {
        if (j==0 || S[i]==T[i])
        {
            ++i;
            ++j;
        }
        else 
        {
            j=next[j];
        }
    }
    if(j>T[0])
        return i-T[0];
    else 
        return 0;
}
//get_next时间复杂度O(m),index_KMP时间复杂度O(n),整个算法时间复杂度O(m+n)。
  • 算法改进:用首位next[1]的值取取代它相等的字符后续next[j]的值。
void get_nextval(String T, int *nextval)
{
    int i,j;
    i=1;
    j=0;
    nextval[1]=0;
    while(i<T[0])
    {
        if(j==0 || T[i]==T[j])
        {
            ++i;
            ++j;
            if(T[i]!=T[j])     /*若当前字符与前缀字符不同*/
                nextval[i]=j;  /*则当前的j为nextval在i位置的值*/
            else
                nextval[i]=nextval[j]; /*如果相同,则将前缀字符nextval值
                                         赋给nextval在i位置的值*/
        }        
    }
}
  • nextval数组值推导:先计算处next数组,看当前j处的字符,查以next[j]下标的字符,若相等就用人家的next[]值,否则保持自己的next[]值。

总结:

  • 串是线性表的扩展,对串这种结构更多的是关注它子串的应用问题。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值