数据结构(串)

1. 串

串的定义:

  • 是由零个或多个字符组成的有限序列,又名叫字符串。
  • 串是一种内容受限制的线性表,其特殊性体现在数据元素是一个字符。

零个字符的串,称为空串,其长度为零。
由一个或多个空格组成的串,称为空格串,其长度为空格字符的个数。
通常称字符在序列中的序号为该字符在串中的位置。

(1) 串的比较

给定两个串,s = "a1a2.....an",t="b1b2....bm",当满足以下条件之一时,s<t。

  1. n<m,且ai = bi(i=1,2,.....,n)。例如,s="hap",t="happy",就有s<t。
  2. 存在某个k<=min(m,n),使得ai = bi(i=1,2,.....,k-1),ak < bk。例如,s="happen",t="happy",因为两串前4个字母均相同,而两串第5个字母(k值), e 的ASCII码是101,而 y 的ASCII码是121,显然 e < y,所以s<t。

(2) 串的存储结构

1. 串的顺序存储

用一组地址连续的存储单元来存储串中的字符序列。一般用定长数组为每个定义的串变量分配一个固定长度的存储区。这样的存储方式存在问题,因为定长,在字符串操作时候,比如连接、插入新串、替换等操作时,都可能使串序列的长度超过了数组的长度MaxSize。

//串的定长顺序存储结构
#define MAXLEN 255
typedef struct {
    char ch[MAXLEN+1]; //存储串的一维数组
    int length;//串的当前长度
}SString;

堆(Heap)的自有存储区,可为每一个新产生的串动态分配一块实际串长所需的存储空间,若分配成功,则返回一个指向起始地址的指针,作为串的基址。

//串的堆式顺序存储结构
typedef struct {
    char *ch;//若是非空串,则按串长分配存储区,否则ch为NULL
    int length;//串的当前长度。
}HString;

2.串的链式存储

与线性表相似,但因为串中每个元素都是一个字符,如果用链表存储串值,一个结点对应一个字符,就会存在很大的空间浪费。因此,一个结点可以存放一个字符,也可以考虑存放多个字符,最后一个结点若是未被占满,可用“#”或其他非串值字符补全。
这里后面算法描述当中所有顺序存储的字符串都是从下标为1的数组分量开始存储的,下标为0的分量闲置不用。

//串的链式存储结构
# define CHUNSIZE 80 //可由用户定义的快大小
typedef struct Chunk{
    char ch[CHUNKSIZE];
    struct Chunk *next;
}Chunk;
typedef struct {
    Chunk *head,*tail;//串的头和尾指针
    int length;//串的当前长度
}LString;

(3) 串的模式匹配算法

  1. 子串的定位运算通常称为串的模式匹配或串的匹配。
  2. 串的模式匹配设有两个字符子串,设S为主串,也称正文串,设T为子串,也称为模式。在主串中查找与模式T相匹配的子串,如果匹配成功,确定相匹配的子串的第一个字符在主串S中出现的位置。

1. BF算法

模式匹配不一定从主串的第一个位置开始,可以指定主串中查找的起始位置pos。
算法步骤:

  1. 分别利用计数指针i和j指示主串S和模式T中当前正待比较的字符位置,i的初始值为pos,j的初始值为1。
  2. 若果两个串均未比较到串尾,即i和j均分别小于等于S和T的长度时,则循环执行以下操作:
  • S.ch[i]和T.ch[j]比较,若相等,则i和j分别指示串中下个位置,继续比较后续字符。
  • 若不等,指针后退重新开始匹配,从主串的下一个字符(i= i-j+2) =>{ i = i -(j-1)+1}起重新和模式的第一个字符(j=1)匹配。
  1. 如果j>T.length,说明模式T中的每一个字符依次和主串S中一个连续的字符序列相等,则匹配成功,返回和模式T中第一个字符串相等的字符在主串S中的序号。(i-T.length)否则不成功,返回0。
int Index_BF(SString S,SString T,int pos)
{
    int i = pos; //初始化
    int 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;//匹配失败
}

BF算法时间复杂度分析如下:

  1. 最好情况下,每趟不成功的匹配都发生在模式串的第一个字符与主串中相应字符的比较。
    例如:
    S = "aaaaaba";
    T = "ba"

设主串长度为n,子串的长度为m,假设从主串的第i个位置开始与模式匹配成功,则在前i-1t趟匹配中字符总共比较i-1次,若第i趟成功的字符比较次数为m,则总比较次数为i-1+m。相对与成功匹配主串,其起始位置由1到n-m+1,假定这n-m+1个起始位置上匹配成功的概率相等,则最好的情况下匹配成功的平均比较次数为:



即最好的情况下的平均时间复杂度为O(n+m)

  1. 最坏的情况下,每趟不成功的匹配都发生在模式串的最后一个字符与主串中相应字符的比较。
    例如:
    S = "aaaaab"
    T = "aab"

假设从主串的第i个位置开始与模式匹配成功,则前i-1趟匹配中字符总共比较了(i-1)*m次,若第i趟成功的比较次数为m,则总共比较次数im。因此最坏情况下匹配成功的平均比较次数为:


即最坏的情况下平均时间复杂度为O(n
m)。

2. KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。

当主串的第i个字符与模式串中第j个字符匹配失败时,主串的第i个字符(i指针不回溯)与模式串的第k(k<j )个字符比较,则模式中的前k-1个字符串必需满足下列关系式:


若令next[j] = k,则next[j]表明模式中第j个字符与主串相应字符失配时,在模式中需要重新和主串字符进行比较的字符位置,由此引出模式串中next函数的定义:




next数组值推导

如果前后缀一个字符相等,k值是2,两个字符相等k值是3,n个字符相等k值就是n+1。

KMP 算法描述

void get_next(String T, int next[]) 
{
    int i,j;    
    i=1;    
    j=0;    
    next[1]=0;      
    while (i<T.length)   
    {       
        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;        /* i用于主串S中当前位置下标值,若pos不为1,则从pos位置开始匹配 */    
    int j = 1;          /* j用于子串T中当前位置下标值 */    
    int next[255];      /* 定义一next数组 */ 
    get_next(T, next);  /* 对串T作分析,得到next数组 */   
    while (i <= S.length && j <= T.length) /* 若i小于S的长度并且j小于T的长度时,循环继续 */    
    {       
        if (j==0 || S.ch[i] == T.ch[j])     /* 两字母相等则继续,与朴素算法增加了j=0判断 */        
        {  
            ++i; ++j;
        }           
        else 
        {           /* 指针后退重新开始匹配 */            
            j = next[j];/* j退回合适的位置,i值不变 */ 
        }
     }  
     if (j > T.length)  return i-T.length;  //匹配成功
    else  return 0;//匹配失败
}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,077评论 0 2
  • 有深爱者必有和气,有和气者必有愉色,有愉色者必有婉容。--------《礼记》 爷爷外号王大炮,顾名思义...
    凭海临风0615阅读 999评论 0 0
  • 早晨收到朋友关爱的信息,和两位闺蜜姐姐带着暖暖的爱和我最爱吃的水果来我家看我,我激动的忘了给她们倒水,官姐姐最近状...
    一路走去阅读 213评论 3 0
  • 一位仁慈善良的出家之人,一位本领高强、尽管有二点顽劣却仍不失尊食的英雄,一位憨厚,却也喜欢搬弄是非的好色之徒,一...
    浊臣阅读 272评论 0 0