攻克数据结构和算法——第三天:串

简介: 一般来说,计算机硬件结构反映处理数值计算需要,而计算机上非数值处理的对象,大多就是字符串数据

一,串的基本操作


串是一种特殊的线性表。


特殊性在于:线性表的数据元素限定为字符集


一般来说,计算机硬件结构反映处理数值计算需要,而计算机上非数值处理的对象,大多就是字符串数据


1,串的定义:


是由零个或多个字符组成的有限序列。


空串:是指长度n=0的串,它不包含任何字符。



空格串:是仅由一个或多个空格组成的串,长度大于等于1。



子串:串中任意个连续的字符组成的子序列。



主串:包含子串的串相应地称为主串。



位置:字符在序列中的序号。子串在主串中的位置则以子串的第一个字符在主串中的位置来表示。



相等:两个串的长度相等,并且对应位置的字符都相等。


串和线性表的逻辑结构极为相似,区别为,串的数据对象约束为字符集。


但是在操作时却有着很大的差别,一般来说,线性表一般将单个数据作为处理对象,而串一般将串整体或者串的一部分作为操作对象。


2,串的基本操作:


1)插入


StrInsert(S, pos, T)

初始条件:串S和T存在, 1 <= pos <= StrLength(S)+1。

操作结果:在串S的下标为pos的字符之前插入串T。


2)删除


StrDelete(S, pos, len)

初始条件:串S存在, 1 <= pos <= Length(S)

且O <= len <= Length(S)- pos +1。

操作结果:从串S中删除下标为pos的字符起长度为len的子串。


3)连接


StrCat(S,T)

初始条件:串S和T存在。

操作结果:返回由S和T联接而成的新串。


4)找子串


StrIndex(S, pos, T)

初始条件:主串S和T存在,T是非空串

操作结果:若主串S中存在和串T值相同的子串,则返回它在主串

S中从第pos个字符开始第一次出现的位置;否则函数值为0。


5)串替换


StrReplace(S, T, V)

初始条件:串S,T和V均已存在, 且T是非空串。

操作结果:用V替换主串S中出现的所有与(模式串) T相等的不重叠的子串。


二,串的模式匹配


8eeb706dc8bc4c9a8f1f293d82cf1b03.png


串的模式匹配(串匹配):找子串在主串中从第pos个字符后首次出现的位置(字串的定位操作)


 08372293baa5489f88ea0d08b4f0676c.png


主串:目标串


子串:模式串


1,BF模式匹配算法(简单匹配算法)


主串S中的子串与模式串T进行比较,直到找到相同的子串为止。


如果存在相同的子串,则匹配成功,返回子串在主串S中的位置pos否则匹配不成功。


子串与模式的比较策略:从前到后依次进行比较。


de6e9c2c8d7f427687de30e461e6cc00.png


cff8cbe5387f4659ab650272598367d8.png

cce825ae5ab94c98b2003850cdc54fff.png


代码实现:


int Index(SStringS, int pos, SString T)
{ int i=pos, j=1;
    while(i <= S.len && j <=T.len)
    { 
        if(S.ch[i] == T.ch[jl)
        {     ++i;
          ++j;
        }
        else
        { 
            i= i-j+2; 
            j= 1;
        }
    }   
        if(j> T.len)
            return i- T.len;
        else
            return 0; 
    }
}


时间复杂度:o(m*n)


2,KMP匹配算法


KMP算法: KMP为(D.E.Knuth 与J.H.Morris和V.R.Pratt)同时发现的算法,因此人们称之为克努特-莫里斯-普拉特算法。


特点:在匹配的过程中,不需要回溯主串的指针i,且时间复杂度可以达到O(m+n)。


在主串中设置指示器i表示主串S中当前比较的字符;在模式串T中设置指示器j表示模式串T中当前比

较的字符。


从主串的第pos个字符起和模式串的第一个字符比较


相等:继续逐个比较后续字符i++; j++;


j==0:继续逐个比较后续字符


j==0表示当前比较的是模式串首字符且不匹配应从主串后继字符起从头比较


不相等:从主串的位置不改变和模式串的第next[j]字符比较        j=next[j];


int Index_ KMP(SString S, int pos, SString T)
{ int i= pos, j=1;
    while (i <= S.len &&j <=T.len)
    {
        if(j==0 || S.ch[i] == T.ch[j])
        {
            ++i; 
            ++j;
        }
        else
            j = next[j];
    if (j > T.len)
        return i- T.len;
    else
        return 0;
}


3,KMP算法相比于简单模式匹配算法的改进之处是什么?


KMP算法的改进之处是它对子串做了一定的处理,使得每次匹配失败后,下一次匹配开始的位置尽可能的向后移。


KMP算法中,每当一趟匹配过程中出现失配时,主串S中的i指针不需要回溯,而是利用已经得到的“部分匹配”结果,将模式串向右“滑动”尽可能远的一段距离后,继续进行比较,从而快速达到匹配结果。


我们要在已获得的信息中做到不遗漏的同时尽可能多的四配---->A子串的后缀集合与B子串的前缀集合的交集里:最长的那个元素----->使B串后移的最少且在已知信息中四配的最多


最长元素的长度:就是j指针回溯的位置


A子串的后缀集合与B子串的前缀集合的交集里---->B子串的前缀集合与它本身的后缀集合的交集


j指针回溯的位置=B子串的最长公共前后缀


通过隐藏信息:匹配失败时A串与B串存在着一段相同的子串


j指针回溯的位置只与B串有关,与A串无关


在A、B字符串匹配之前就通过B串计算回溯位置存在一个数组next里

next与B串形成映射数组存的数据next[i]就是B[1]~B[i]的最长公共前后缀的长度


求next值


B串自己与自己匹配B[1]~B[i]的前缀与它的后缀匹配后缀为主串前缀为模式串


递推的方式求出next数组


目的:对于i属于[2,m]求出B[1]~B[i]的最长公共前后缀的长度


1.如果匹配:


next[j]=j+1 , j是B串前缀的指针


也就是当前字符匹配之前的最长公共前后缀的长度这里匹配成功了,要+1


2。如果不匹配,回溯j指针,j=next[j]直到匹配成功


KMP一次比较只会产生两种结果


1.匹配成功主串的指针向前移动


2.四配败模式串整体向前移动9


前面的移动必为n次,后面的移动最多n次


所以最大的时间复杂度为2n


同理也可得


next数组构建最大时间复杂度为2m


4,next值的计算思想:


由模式串本身发现。


3cc04b7cc5c649dab28d9d401a3a86b6.png


5,为什么模式串的next值和主串无关?


每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 i指针,而是利用已经得到的“部分匹配”的结果将模式子串向右“滑动”尽可能远的一段距离后,继续进行比较。如果 ok,那么主串的指示指针不回溯!算法的时间复杂度只和子串有关


6,


next值的计算仅和模式串本身有关


next[ ]是对模式串p进行预处理后的查询表


预处理也就是在模式p和目标进行匹配操作之前进行的操作对p而言,它并不关心要匹配的堤什么样子的,它首先要做的是很好的了解自己,也就是对自身模式的了解


2d3854ede78d4a189f93e1bc2420b21f.png


这实际上也是一个匹配的过程,不同在于:主串和模式串是同一个串


代码实现:


void Get_ Next(SString T, int next[])
{ int j=1, k =0;
    next[1]=0;
    while (j < T.len )
        { if(k==0 || T.ch[j] == T.ch[k] )
            {     ++j;
                ++k;
                next[j]=k;
            }
          else
                k = next[k]; 
    }
}


7,


当Si和Tj比较后,发现两者不相等时,但Tj和Tk相等,那就意味着Si和Tk不需要进行额外的比较了,因此j位置的nextval值仍然时k,即nextval[j] = next[j]。


void Get_ Nextval(SString T, int next[ ],int nextval[ ])
{
    int j= 2, k=0;
    Get_ Next(T, next);
    nextval[1]=0;
    next[i]=0;
    while(j <= T.len )
    {
        k=next[j];
        if( T.ch[j] == T.ch[k] )
            nextval[j]=nextval[k];
        else
            nextval[j =next[ij] ;
        j++;
    }
}


模式串的长度m比主串的长度n要小很多,且计算next或nextva1函数的时间复杂度为0(m)。因此,对于整个匹配算法来说,所增加的计算next或nextval是值得的。

目录
相关文章
|
2月前
|
存储 监控 安全
企业上网监控系统中红黑树数据结构的 Python 算法实现与应用研究
企业上网监控系统需高效处理海量数据,传统数据结构存在性能瓶颈。红黑树通过自平衡机制,确保查找、插入、删除操作的时间复杂度稳定在 O(log n),适用于网络记录存储、设备信息维护及安全事件排序等场景。本文分析红黑树的理论基础、应用场景及 Python 实现,并探讨其在企业监控系统中的实践价值,提升系统性能与稳定性。
59 1
|
2月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
60 0
|
6月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
175 10
 算法系列之数据结构-二叉树
|
6月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
146 3
 算法系列之数据结构-Huffman树
|
6月前
|
算法 Java
算法系列之数据结构-二叉搜索树
二叉查找树(Binary Search Tree,简称BST)是一种常用的数据结构,它能够高效地进行查找、插入和删除操作。二叉查找树的特点是,对于树中的每个节点,其左子树中的所有节点都小于该节点,而右子树中的所有节点都大于该节点。
173 22
|
7月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
184 30
|
7月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
285 25
|
7月前
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
264 23
|
22天前
|
机器学习/深度学习 算法 新能源
【优化调度】基于matlab粒子群算法求解水火电经济调度优化问题研究(Matlab代码实现)
【优化调度】基于matlab粒子群算法求解水火电经济调度优化问题研究(Matlab代码实现)
|
24天前
|
算法 机器人 定位技术
基于机器视觉和Dijkstra算法的平面建筑群地图路线规划matlab仿真
本程序基于机器视觉与Dijkstra算法,实现平面建筑群地图的路径规划。通过MATLAB 2022A读取地图图像,识别障碍物并进行路径搜索,支持鼠标选择起点与终点,最终显示最优路径及长度,适用于智能导航与机器人路径规划场景。

热门文章

最新文章