数据结构面试之十四——字符串的模式匹配

简介: 第(1)步;从主串S的第pos个字符和模式的第一个字符进行比较之,若相等,则继续逐个比较后续字符;否则从主串的下一个字符起再重新和模式串的字符比较之。第(2)步骤;依次类推,直至模式T中的每一个字符依次和主串S中的一个连续的字符序列相等,则称匹配成功;函数值为和模式T中第一个字符相等的字符在主串S中的序号,否则称为匹配不成功,函数值为0。

数据结构面试之十四——字符串的模式匹配

题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。

十四、字符串的模式匹配


1.       模式匹配定义——子串的定位操作称为串的模式匹配。


2.       普通字符串匹配BF算法(Brute Force 算法,即蛮力算法)


【算法思想】:


第(1)步;从主串S的第pos个字符和模式的第一个字符进行比较之,若相等,则继续逐个比较后续字符;否则从主串的下一个字符起再重新和模式串的字符比较之。


第(2)步骤;依次类推,直至模式T中的每一个字符依次和主串S中的一个连续的字符序列相等,则称匹配成功;函数值为和模式T中第一个字符相等的字符在主串S中的序号,否则称为匹配不成功,函数值为0。


比如对于主串S=”abacababc”; 模式串T=”abab”; 匹配成功,返回4。


对于主串S=”abcabcabaac”; 模式串T=”abab”; 匹配不成功,返回0。



【算法实现】:


//普通字符串匹配算法的实现


int Index(char* strS, char* strT, int pos)

{  

     //返回strT在strS中第pos个字符后出现的位置。

       int i = pos;

      int j = 0;

      int k = 0;

      int lens = strlen(strS);

      int lent = strlen(strT);

      while(i < lens && j < lent)

      {

             if(strS[i+k] == strT[j])

             {

                 ++j;    //模式串跳步

                    ++k;    //主串(内)跳步

               }

             else

             {

                 i = i+1;

                 j=0;  //指针回溯,下一个首位字符

                   k=0;

             }

      }//end i

       if(j >= lent)

      {

         return i;

      }

      else

      {

         return 0;

      }

}//end

[算法时间复杂度]:设主串长度为m,模式串的长度为n。一般情况下n<m。


最好时间复杂度:举例,主串S=”ababaababc”; 模式串T=”abab”; 比较次数为n次。时间复杂度为O(n)。


最坏时间复杂度:举例,主串S=”000000000000000000001”(20个0,1个1); 模式串T=”00001”(4个0,1个1);比较次数为17*5次。时间复杂度接近O(m*n)。整个匹配过程需要多次回溯(有16次回溯)。


平均时间复杂度:O(m*n)。


[空间复杂度]:O(1),不需要额外开辟空间存储。



3.         KMP算法 ——是一种线性时间复杂的字符串匹配算法,它是对BF算法改进。


[时间复杂度]:O(m+n),即:O(strlen(S) + strlen(T))


[空间复杂度]:O(n),即:O(strlen(T))


【核心思想】:是利用已经得到的部分匹配信息来进行后面的匹配过程。


正文t


t1


t2


t3



tm



tn


模式p


p1


p2


p3


….


pm



.



【next(j)定义】:表示当pi不等于tr时,下一次将pnext[i] 与tr开始继续后继对应字符的比较。


其中next[0]=-1,表明当p0不等于tr时,将从p-1与tr开始继续后继对应字符的比较;显然p-1是不存在的,我们可以将这种情况理解成下一步将从p0与tr+1开始继续后继对应字符的比较。


举例说明1:模式串p=“google”,对应的next[j]={-1,0,0,0,1,0}。


解读:


g


设定为-1


o


字符o之前没有匹配的字符。


o


字符o(第2个)之前的字符(g,o)不同。


g


字符g之前的字符(g,o,o)前缀、后缀(如:g与o;go与oo)不匹配。


l


字符l之前的字符(g,o,o,g)前缀、后缀(如:g与g)相同,返回1。


e


字符e之前的字符(g,o,o,g,l)前缀、后缀(如:goo与ogl)不同。


举例说明2:模式串p=“abaabcaba”,对应的next[j]={-1,0,0,1,1,2,0,1,2}。


【KMP算法实现】:


第一步:求解next数组。


typedef struct

{

      char str[100];

      int length;

}seqString;

//根据模式t的组成求其对应的next数组。

void getNext(seqString t, int next[])

{

     next[0] = -1;

      int i = 0;

      int j = -1;

      while(i < t.length)

      {

             if(j == -1 || t.str[i] == t.str[j])

             {

                    ++i;

                    ++j;

                    next[i] = j;

             }

             else

             {

                    j = next[j];

             }

      }//end while

      cout << "next[ "<< t.length << " ]" << endl;

      for(i = 0; i < t.length; i++)

      {

             cout << next[i] << "\t";

      }

     cout << endl;

}//end


第二步:KMP匹配算法的实现。


//t代表正文源串,p代表模式匹配串,next代表匹配next数组


int kmp(seqString t, seqString p, int next[])

{

      int i = 0;

      int j = 0;

     while(i < t.length && j < t.length)

      {

             if(j == -1 || t.str[i] == p.str[j])

             {

                   i++;

                   j++;

             }

             else

             {

                   j = next[j];

             }

      }

      if(j == p.length)

      {

            return( i -p.length);

      }

      else

     {

             return -1;

      }

}

int main()

{

      int rtnPos = 0;

      seqString strS;

      strcpy(strS.str,"goodgoogle");    //源串

   strS.length = strlen(strS.str);

      seqString strT;

      strcpy(strT.str,"abaabcaba");     //模式串

   strT.length = strlen(strT.str);

      int *pNext = new int[strT.length];

     getNext(strT,pNext);

      rtnPos = kmp(strS,strT,pNext);

      cout << rtnPos << endl;        //输出匹配位置

      return 0;

}


4.       手动演示BF算法与KMP算法的不同(如下图所示)。

image.png



字符串的匹配不是很好理解,JULY曾经用很长的篇幅去讲,大家可以参考。很多材料讲的思路一致,但实现稍有差别,本文的实现和图示是一致的,有错误的话希望大家提出,不胜感激!


相关文章
|
1天前
|
XML JSON NoSQL
Redis的常用数据结构之字符串类型
Redis的常用数据结构之字符串类型
21 0
|
1天前
|
设计模式
大厂求职者必看!如何用简单工厂模式征服面试官?
大厂求职者必看!如何用简单工厂模式征服面试官?
7 0
|
1天前
|
存储 算法 安全
【刷题】 leetcode 面试题 01.06 字符串压缩
来看效果: 非常好!!!过啦!!!
25 5
【刷题】 leetcode 面试题 01.06 字符串压缩
|
1天前
|
算法 搜索推荐 大数据
数据结构面试常见问题
V哥在工作中整理了22个常用数据结构实现与原理分析,在面试中可以帮你你充分准备
|
1天前
|
存储 NoSQL 安全
Redis入门到通关之数据结构解析-动态字符串SDS
Redis入门到通关之数据结构解析-动态字符串SDS
18 0
|
1天前
|
存储 安全 索引
Day5 长篇:字符串和常用数据结构
Day5 长篇:字符串和常用数据结构
6 0
|
1天前
|
存储 Go 开发者
Golang深入浅出之-Go语言字符串操作:常见函数与面试示例
【4月更文挑战第20天】Go语言字符串是不可变的字节序列,采用UTF-8编码。本文介绍了字符串基础,如拼接(`+`或`fmt.Sprintf()`)、长度与索引、切片、查找与替换(`strings`包)以及转换与修剪。常见问题包括字符串不可变性、UTF-8编码处理、切片与容量以及查找与替换的边界条件。通过理解和实践这些函数及注意事项,能提升Go语言编程能力。
28 0
|
1天前
面试题 01.06. 字符串压缩
面试题 01.06. 字符串压缩
7 0
|
1天前
|
算法 搜索推荐 Python
数据结构与算法在Python面试中的应用实例
【4月更文挑战第13天】本文聚焦Python面试中的数据结构与算法问题,包括排序算法、链表操作和树图遍历。重点讨论了快速排序、链表反转和二叉树前序遍历的实现,并指出理解算法原理、处理边界条件及递归操作是避免错误的关键。通过实例代码和技巧分享,帮助面试者提升面试表现。
13 0
|
1天前
|
Python
python学习-函数模块,数据结构,字符串和列表(下)
python学习-函数模块,数据结构,字符串和列表
73 0