邓俊辉 数据结构与算法C++版 第十三章 串 ADT

ADT

定义和特点

特点:串长远远大于字符种类
串定义和特点

术语

substr()

ADT接口实现

ADT接口实现

模式匹配

问题与需求

主要解决4个问题。
四个问题解决

算法测试方法

成功与失败的情况分开来测试。
成功与失败的情况分开

蛮力匹配

构思

  • 自左向右,以字符为单位,依次移动模式串直到在某个位置发现匹配
    在这里插入图片描述

蛮力匹配:版本1

  • return i-j

i-j 大于n-m 即可判定失败。因为失败时 (字符串T)i=n,(模板串P)j<m。
i-j<=n-m 即可判定成功
i-j的意义是字符串中与模板串开始比较的位置。

蛮力匹配版本1

蛮力匹配:版本2

蛮力2
上述算法有个细节

if( m <= j ) break;

直接跳出当前for(i。。。。i++)的循环,而 当前循环的i 加1操作没有执行!
当返回值 i = n - m 时配对成功,当 i =n - m + 1 时配对失败

蛮力匹配:性能分析

  1. 最好最坏情况复杂度分析

在这里插入图片描述

  1. 最坏情况例子

字母表={0,1}
成功次数m-1次,失败次数1
在这里插入图片描述

  1. 特点
    在这里插入图片描述

  2. 蛮力算法为何低效

  • 有重复匹配的前缀:即一个字符串T中的一个位置需要比对多次。
  • 不变性:模板P总是右移一位去比对。不会根据情况改变

KMP算法

next【】表

在这里插入图片描述

理解next【】表

理解N ( p , j )={ 0 <= t<j | P [ 0, t ) == P[ j-t , j ) };
next【】表

  1. 具有特点:
    快速右移
    避免回溯
    next【0】=-1

构造next【】表

next【j】的意思感觉下图说的不对,应该是最大自匹配的真前缀和真后缀的长度减1

  1. 当p【j】== p【 next [ j ] 】

计算next【j+1】
在这里插入图片描述

  1. 当p【j】!= p【 next [ j ] 】时

在这里插入图片描述

next【】表代码实现

在这里插入图片描述

KMP复杂度准确分析 ——线性时间O(n)

设一个k=2i-j (分析方法忽略吧)
在这里插入图片描述

KMP算法改进版

KMP算法为改进前的重复比对

改进的原因:在当前模式串P的元素重复,使得连续与文本串T的元素进行对比失败,产生冗余,故可跳过相同元素与T的重复比对,提高KMP算法性能
在这里插入图片描述

BM_BC算法

动机——bc【】表

因为字符串配对过程中,大部分情况失败的概率远远大于成功的概率,故可采用bc表。next表善于利用配对成功的字符(经验),而bc表 注重配对失败的教训可以帮助我们排除更多字符。

坏字符

在这里插入图片描述

  • 如何右移
    试图从模式串P中找到一个X(与文本串T相同的X),使这个X能与T中的X对齐。P串偏移的距离存入bc【】表中。

  • 偏移距离如何确定
    位移量取决于适配位置 j,以及X在模式串P中的秩,而与T和i无关。

    • 由此引申因偏移距离确定发生的三种情况
      在这里插入图片描述
      在这里插入图片描述
    1. 从模式串P中能找到一个或多个X(与文本串T相同的X) , 要位移量尽可能小,则要选择模式串P中X的 秩最大的(也可以说从最大秩开始 后面没有X了)且偏移量 j - bc [ ’ X ’ ] >0
    2. 从模式串P中不能找到一个X(与文本串T相同的X),则通过最左侧哨兵通配符" * ",将模式串P完整的移过X(文本串T中的X)这个位置。
    3. 虽然从模式串P找到X 但X过于靠右即秩过大,会造成偏移量 j - bc [ ’ X ’ ] <0 ,故只需将模式串P 向后一个位置。

构造bc【】

画家策略:模式串P中自左向右,每次出现对应的字符则更新最大秩

/*DSA*/#include "string_pm/string_pm_test.h"

//*****************************************************************************************
//    0                       bc['X']                                m-1
//    |                       |                                      |
//    ........................X***************************************
//                            .|<------------- 'X' free ------------>|
//*****************************************************************************************
int* buildBC ( char* P ) { //构造Bad Charactor Shift表:O(m + 256)
   int* bc = new int[256]; //BC表,与字符表等长
   for ( size_t j = 0; j < 256; j ++ ) bc[j] = -1; //初始化:首先假设所有字符均未在P中出现
   for ( size_t m = strlen ( P ), j = 0; j < m; j ++ ) //自左向右扫描模式串P
      bc[ P[j] ] = j; //将字符P[j]的BC项更新为j(单调递增)——画家算法
   /*DSA*/printBC ( bc );
   return bc;
}

最好情况—— O(n/m)

在这里插入图片描述
在当前情况下,p一共移动 n/m次就停止,所以最好情况是 O(n/m)
在这里插入图片描述

最坏情况—— O(n*m)

因为p从最末尾开始
在这里插入图片描述
可以发现BM算法也并非完美,故将BM_BC+KMP算法结合则会产生完美的算法

BM_GS算法

好后缀策略

概念

在这里插入图片描述

在这里插入图片描述

实例

在这里插入图片描述

构造GS表

  • 构造gs表的过程:MS【】——> ss【】——>gs【】
  • MS【】的含义↓

在这里插入图片描述
在这里插入图片描述
注意3 和 4的含义。

  • “I C E”与后缀最长匹配者
    ss【j】= MS【j】的长度= 3

  • “RICE”与后缀最长匹配者
    ss【j】= MS【j】的长度= 4

在这里插入图片描述

性能

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
注:n为文本串T的长度,m为模式串P的长度
蛮力算法BF:单次比对成功率越高其复杂度越高,最好O(n+m),最坏O(nm)
KMP算法:稳定一直保持线性时间复杂度O(n+m)
BM算法_BC:适合大字符集、单次比对失败率较高的情况,最好O(n/m),最坏O(n
m)
BM算法_BC+GS:充分补偿了BC的缺陷。故最好是O(n/m),最坏是O(n+m)

Karp-Rabin 算法:串即是数

凡物皆数

任何串都可以用 素数序列转化为自然数 N 表示出来。
在这里插入图片描述

串亦是数

在这里插入图片描述
若视作自然数比对,只需O(1)时间。

数位溢出

70bit???????不懂
70位的128进制才能表示十个位的自然数。咋算的?
在这里插入图片描述

散列压缩

基本构思:通过对比压缩后的指纹确定匹配位置,故只需要O(1)时间比对
在这里插入图片描述

散列冲突

有散列就有概率发生冲突,即有两个自然数%M 得到的值相等。
因此经过hash()筛选之后,还需要进行严格比对,方可确定是否匹配。
在这里插入图片描述
离Karp-Rabin算法只剩最后一步。。。

快速指纹计算

在这里插入图片描述

在这里插入图片描述
Karp-Rabin代码解析参考:https://blog.csdn.net/Shine__Wong/article/details/102095474
代码涉及将串转为数且要除余,来限定范围。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值