数据结构 | 第11章 串 - 概述

208 阅读3分钟

第11章 串

串或字符串 (string) 属于线性结构,自然地可直接利用向量或列表等序列结构加以实现,但字符串作为数据结构,特点也极其鲜明,这可归纳为:结构简单,规模庞大,元素重复率高。

以字符串形式表示的海量文本数据的高效处理技术,一直都是相关领域的研究重点。

11.1 串及串匹配

11.1.1 串

  • 字符串

    一般由n个字符构成的串记作:S = "a1a2 ... an-1",其中,a1 ∈ Σ,0 ≤ i ≤ n

    这里的Σ是所有可用字符的合集,称作字符表 (alphabet) 。例如ASCII字符集、组成蛋白质所有氨基酸等等。

    字符串S所含字符的总数n,称作S的长度,记|S| = n。长度为0的串称作空串 (null string) 。

  • 子串

    字符串中任一连续的片段,称作其字串 (substring) 。

    由字符串 S 中起始于位置 i 的连续 k 个字符组成的子串记作:

    S.substr(i, k) = "ai ai+1 ... ai+k-1" = S[i, i + k)

    起始于位置0、长度为 k 的子串称为前缀(prefix):

    prefix(S, k) = S.substr(0, k) = S[0, k)

    终止于位置n - 1、长度为k的字串称为后缀 (suffix):

    suffix(S, k) = S.substr(n - k, k) = S[n - k, n)

    空串是任何字符串的子串,也是任何字符串的前缀和后缀;任何字符串都是自己的子串,也是自己的前缀和后缀。

  • 判等

    字符串S[0, n)和T[0, m)称作相等,当且仅当二者长度相等(n = m),且对应的字符分别相同:S[i] = T[i]

  • ADT

    image.png

    image.png

11.1.2 串匹配

  • 应用与问题

    在涉及字符串的众多实际应用中,模式匹配是最常用的一项基本技能。 比如UNIX Shell的grep工具 ( General Regular Expression Parser, 通用正则表达式解析器) 和DOS(这里指磁盘操作系统)的find命令,基本功能都是在指定的字符串中查找特定模式的字符串

    这类操作都属于串模式匹配 ( string pattern matching ) 范畴,简称串匹配。

    一般地,即:

    对基于同一字符表的任何文本串T( |T| = n )和模式串P ( |P| = m ) :

    判定T中是否存在某一子串与P相同

    若存在(匹配),则报告该子串在T中的起始位置。

  • 问题分类

    image.png

    Ctrl + F的查找就是模式枚举。

11.1.3 测评标准与策略

如何对任一串匹配算法的性能作出客观的测量和评估?

本章采用的评价标准:随机选取文本串T,并从T中随机取出长度为m的子串作为模式串P。

11.2 蛮力算法

11.2.1 算法描述

image.png

初始状态下,T的前m个字符将与P的m个字符两两对齐。

只有在某一轮的m次比对全部成功之后才成功返回。

11.2.2 算法实现

蛮力串匹配算法(版本一):

 0001 /******************************************************************************************
 0002  * Text     :  0   1   2   .   .   .   i-j .   .   .   .   i   .   .   n-1
 0003  *             ------------------------|-------------------|------------
 0004  * Pattern  :                          0   .   .   .   .   j   .   .
 0005  *                                     |-------------------|
 0006  ******************************************************************************************/
 0007 int match ( char* P, char* T ) { //串匹配算法(Brute-force-1)
 0008    size_t n = strlen ( T ), i = 0; //文本串长度、当前接受比对字符的位置
 0009    size_t m = strlen ( P ), j = 0; //模式串长度、当前接受比对字符的位置
 0010    while ( j < m && i < n ) //自左向右逐个比对字符
 0011       if ( T[i] == P[j] ) //若匹配
 0012          { i ++;  j ++; } //则转到下一对字符
 0013       else //否则
 0014          { i -= j - 1; j = 0; } //文本串回退、模式串复位
 0015    return i - j; //如何通过返回值,判断匹配结果?
 0016 }

蛮力串匹配算法(版本一):

 0001 /******************************************************************************************
 0002  * Text     :  0   1   2   .   .   .   i   i+1 .   .   .   i+j .   .   n-1
 0003  *             ------------------------|-------------------|------------
 0004  * Pattern  :                          0   1   .   .   .   j   .   .
 0005  *                                     |-------------------|
 0006  ******************************************************************************************/
 0007 int match ( char* P, char* T ) { //串匹配算法(Brute-force-2)
 0008    size_t n = strlen ( T ), i = 0; //文本串长度、与模式串首字符的对齐位置
 0009    size_t m = strlen ( P ), j; //模式串长度、当前接受比对字符的位置
 0010    for ( i = 0; i < n - m + 1; i++ ) { //文本串从第i个字符起,与
 0011       for ( j = 0; j < m; j++ ) //模式串中对应的字符逐个比对
 0012          if ( T[i + j] != P[j] ) break; //若失配,模式串整体右移一个字符,再做一轮比对
 0013       if ( j >= m ) break; //找到匹配子串
 0014    }
 0015    return i; //如何通过返回值,判断匹配结果?
 0016 }

11.2.3 时间复杂度

蛮力算法至多迭代n - m + 1轮,每轮至多需进行m次比对

故总共做不超过(n - m + 1) · m次比对。因m << n,渐近时间复杂度应为O(n·m)。

image.png

“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 19 天,点击查看活动详情