网站怎么识别PC 手机装修设计灵感网站
news/
2025/10/1 20:06:05/
文章来源:
网站怎么识别PC 手机,装修设计灵感网站,沈阳seo排名优化软件,建设部设计规范网站文章目录
串的模式匹配
考纲内容
复习提示
1.简单的模式匹配算法
知识回顾
2.串的模式匹配算法——KMP算法
2.1字符串的前缀、后缀和部分匹配值
2.2KMP算法的原理是什么
3.KMP算法的进一步优化 串的模式匹配
考纲内容 字符串模式匹配 复习提示 本章是统考大纲第6章内…文章目录
串的模式匹配
考纲内容
复习提示
1.简单的模式匹配算法
知识回顾
2.串的模式匹配算法——KMP算法
2.1字符串的前缀、后缀和部分匹配值
2.2KMP算法的原理是什么
3.KMP算法的进一步优化 串的模式匹配
考纲内容 字符串模式匹配 复习提示 本章是统考大纲第6章内容,采纳读者建议单独作为一章,大纲只要求掌握字符串模式匹配重点掌握 KMP 匹配算法的原理及 next数组的推理过程手工求 next 数组可以先计算出部分匹配值表然后变形或根据公式来求解。了解nextval数组的求解方法。 1.简单的模式匹配算法
子串的定位操作通常称为串的模式匹配它求的是子串(常称模式串)在主串中的位置。这里采用定长顺序存储结构给出一种不依赖于其他串操作的暴力匹配算法。
int Index(sString S,sString T){int i1,j1;while(iS.length jT.length){if(S.ch[i]T.ch[j]){i; j; //继续比较后继字符
} else{ii-j2; j1; //指针后退重新开始匹配
}
}if(jT.length) return i-T.length;else return 0;
}
在上述算法中分别用计数指针 i 和 j 指示主串S和模式串T中当前正待比较的字符位置。
算法思想为:从主串S的第一个字符起与模式串T的第一个字符比较若相等则继续逐个比较后续字符;否则从主串的下一个字符起重新和模式串的字符比较;以此类推直至模式串T中的每个字符依次和主串S中的一个连续的字符序列相等则称匹配成功函数值为与模式串T中第一个字符相等的字符在主串S中的序号否则称匹配不成功函数值为零。图 4.2展示了模式串 Tabcac和主串S的匹配过程每次匹配失败后都把模式串T后移一位。 简单模式匹配算法的最坏时间复杂度为 O(nm)其中n和m分别为主串和模式串的长度。
例如当模式串为0000001而主串为0000000000000000000000000000000000000000 000001时由于模式串中的前6个字符均为0主串中的前 45 个字符均为0每趟匹配都是比较到模式串中的最后一个字符时才发现不等指针i需要回溯 39 次总比较次数为 40x7280 次。
知识回顾 2.串的模式匹配算法——KMP算法
根据图 4.2 的匹配过程在第三趟匹配中i7、j5 的字符比较结果不等于是又从 i4、j1 重新开始比较。然而仔细观察会发现i4和 j1i5 和j1及 i6和 j1 这三次比较都是不必进行的。从第三趟部分匹配的结果可知主串的第4个、第5个和第6个字符是b、c和a(即模式串的第2 个、第3个和第4个字符)因为模式串的第1个字符是a所以无须再和这三个字符进行比较,而只需将模式串向右滑动三个字符的位置,继续进行 i7、j2 时的比较即可。
在暴力匹配中每趟匹配失败都是模式串后移一位再从头开始比较。而某趟已匹配相等的字符序列是模式串的某个前缀这种频繁的重复比较相当于模式串不断地进行自我比较这就是其低效率的根源。因此可以从分析模式串本身的结构着手若已匹配相等的前缀序列中有某个后缀正好是模式串的前缀则可将模式串向后滑动到与这些相等字符对齐的位置主串 i 指针无须回溯并从该位置开始继续比较。而模式串向后滑动位数的计算仅与模式串本身的结构有关而与主串无关(这里理解起来比较困难没关系带着这个问题继续往后看)。
2.1字符串的前缀、后缀和部分匹配值
要了解子串的结构首先要弄清楚几个概念:前缀、后缀和部分匹配值。
前缀指除最后一个字符以外字符串的所有头部子串
后缀指除第一个字符外字符串的所有尾部子串
部分匹配值则为字符串的前缀和后缀的最长相等前后缀长度。下面以ababa为例进行说明:
a的前缀和后缀都为空集最长相等前后缀长度为0。ab的前缀为{a}后缀为{b}{a}n{b}Ø最长相等前后缀长度为 0。aba的前缀为{a,ab}后缀为{a,ba}{a,ab}n{a,ba}{a}最长相等前后缀长度为 1。abab的前缀{a,ab,aba}n后缀{b,ab,bab}{ab}最长相等前后缀长度为 2.ababa的前缀{a,ab,aba,abab}∩后缀{a,ba,aba,baba}{a,aba}公共元素有两个最长相等前后缀长度为3。
因此字符串ababa的部分匹配值为 00123.
这个部分匹配值有什么作用呢? 回到最初的问题主串为 ababcabcacbab子串为abcac。 利用上述方法容易写出子串abcac的部分匹配值为 00010将部分匹配值写成数组形式就得到了部分匹配值(Partial MatchPM)的表。
下面用 PM 表来进行字符串匹配 第一趟匹配过程: 发现 c与a不匹配前面的2个字符ab是匹配的查表可知最后一个匹配字符b对应的部分匹配值为0因此按照下面的公式算出子串需要向后移动的位数: 移动位数已匹配的字符数-对应的部分匹配值 因为2-02所以将子串向后移动2位如下进行第二趟匹配: 第二趟匹配过程: 发现 c与b不匹配前面4个字符abca是匹配的最后一个匹配字符a对应的部分匹配值为14-13将子串向后移动3位如下进行第三趟匹配: 第三趟匹配过程: 子串全部比较完成匹配成功。整个匹配过程中主串始终没有回退所以KMP算法可以在 O(nm)的时间数量级上完成串的模式匹配操作大大提高了匹配效率。
某趟发生失配时若对应的部分匹配值为 0则表示已匹配相等序列中没有相等的前后缀此时移动的位数最大直接将子串首字符后移到主串当前位置进行下一趟比较:若已匹配相等序列中存在最大相等前后缀(可理解为首尾重合)则将子串向右滑动到和该相等前后缀对齐(这部分字符下一趟显然不需要比较)然后从主串当前位置进行下一趟比较。 2.2KMP算法的原理是什么
我们刚刚学会了怎样计算字符串的部分匹配值、怎样利用子串的部分匹配值快速地进行字符串匹配操作但公式“移动位数已匹配的字符数-对应的部分匹配值”的意义是什么呢?
如图4.3所示当c与b不匹配时已匹配abca的前缀a和后缀a为最长公共元素。已知前缀a与 b、c均不同与后缀a相同因此无须比较直接将子串移动“已匹配的字符数-对应的部分匹配值”用子串前缀后面的元素与主串匹配失败的元素开始比较即可如图 4.4所示。 对算法的改进方法: 已知右移位数已匹配的字符数-对应的部分匹配值。 写成Move(j-1)-PM[j-1]。 使用部分匹配值时每当匹配失败就去找它前一个元素的部分匹配值这样使用起来有些不方便所以将 PM 表右移一位这样哪个元素匹配失败直接看它自己的部分匹配值即可。
将上例中字符串abcac的PM 表右移一位就得到了next 数组: 我们注意到: 1) 第一个元素右移以后空缺的用-1来填充因为若是第一个元素匹配失败则需要将子串向右移动一位而不需要计算子串移动的位数。 2) 最后一个元素在右移的过程中溢出因为原来的子串中最后一个元素的部分匹配值是其下一个元素使用的但显然已没有下一个元素所以可以舍去。
这样上式就改写为 Move(j-1)-next[j] 相当于将子串的比较指针 j 回退到 jj-Movej-((j-1)-next[j])next[j]1 有时为了使公式更加简洁、计算简单将next 数组整体1。 因此上述子串的 next 数组也可以写成 [命题追踪——KMP 匹配过程中指针变化的分析]
最终得到子串指针变化公式 jnext[ j ]。在实际匹配过程中子串在内存中是不会移动的而是指针发生变化画图举例只是为了让问题描述得更形象。next[ j ]的含义是:当子串的第 j 个字符与主串发生失配时跳到子串的 next[ j ]位置重新与主串当前位置进行比较。
如何推理 next 数组的一般公式?设主串为模式串为当主串中第 i 个字符与模式串中第 j 个字符失配时,子串应向右滑动多远,然后与模式中的哪个字符比较?
假设此时应与模式串的第k(kj)个字符继续比较则模式串中前 k-1 个字符的子串必须满足下列条件且不可能存在 kk 满足下列条件:
若存在满足如上条件的子串则发生失配时仅需将模式串向右滑动至模式串的第k个字符和主串的第i个字符对齐此时模式串中的前k-1 个字符的子串必定与主串中第i个字符之前长度为 k-1 的子串相等由此只需从模式串的第k个字符与主串的第i个字符继续比较即可,如图 4.5 所示。 当模式串已匹配相等序列中不存在满足上述条件的子串时(可视为 k1)显然应该将模式串右移 j-1 位让主串的第 i 个字符和模式串的第1个字符进行比较此时右移位数最大。
当模式串的第1个字符(j1)与主串的第i个字符发生失配时规定 next[1]0可理解为将主串的第i个字符和模式串的第1个字符的前面空位置对齐即模式串右移一位。将模式串右移一位从主串的下一个位置(i1)和模式串的第1个字符继续比较。
通过上述分析可以得出 next 函数的公式: 上述公式不难理解实际做题求 next 值时用之前的方法也很好求但要想用代码来实现貌似难度还真不小我们来尝试推理求解的科学步骤。 首先由公式可知
next[1]0
设 next[j]k此时k应满足的条件在上文中已描述。 此时 next[ j1 ]?可能有两种情况: (1)若则表明在模式串中
并且不可能存在 kk 满足上述条件此时 next[ j1 ]k1即 next[ j1 ] next[ j ]1
(2)若则表明在模式串中
此时可将求 next 函数值的问题视为一个模式匹配的问题。用前缀 去与后缀 匹配当 时应将向右滑动至以第 next [k]个字符与比较若 与仍不匹配,则需要寻找长度更短的相等前后缀,下一步继续用 与比较,以此类推,直到找到某个更小的 knext[next… [k]](1kkj)满足条件
则 next [ j1 ]k1
也可能不存在任何 k满足上述条件,即不存在长度更短的相等前缀后缀,令 next[ j1 ]1
理解起来有一点费劲?下面举一个简单的例子。
图 4.6的模式串中已求得6个字符的next值,现求 next [7],因为next[ 6 ]3,又则需比较和(因next[ 3 ]1),由于,,而next[1]0,因此
next [7]1求next [8]因,则next [8]next [7]12;求next [9]因,则 next [9]3。 通过上述分析写出求 next 值的程序如下:
void get_next(SString T,int next[]){int i1j0;next[1]0;while(iT.length){if(j0||T.ch[i]T.ch[j]){i; j;next[i]j; //若pipj则 next[j1]next[j]1}elsejnext[j]; //否则令jnext[j]循环继续}
}
计算机执行起来效率很高但对于我们手工计算来说会很难。因此当我们需要手工计算时还是用最初的方法。
与 next 数组的求解相比,KMP 的匹配算法相对要简单很多它在形式上与简单的模式匹配算法很相似。不同之处仅在于当匹配过程产生失配时指针i不变指针 j退回到 next[j]的位置并重新进行比较并且当指针j为0时指针i和j同时加 1。即若主串的第i个位置和模式串的第1个字符不等则应从主串的第 i1个位置开始匹配。具体代码如下:
int Index_KMP(SString S,SString T,int next[]){int i1j1;while(iS.length jT.length){if(j0||S.ch[i]T.ch[j]){i; j; //继续比较后继字符}elsejnext[j]; //模式串向右移动}if(jT.length)return i-T.length; //匹配成功elsereturn 0;
}
[命题追踪——KMP 匹配过程中比较次数的分析]
尽管普通模式匹配的时间复杂度是 O(mn)KMP 算法的时间复杂度是 O(mn)但在一般情况下普通模式匹配的实际执行时间近似为 0(mn)因此至今仍被采用。KMP 算法仅在主串与子串有很多“部分匹配”时才显得比普通算法快得多其主要优点是主串不回溯。 3.KMP算法的进一步优化
前面定义的 next 数组在某些情况下尚有缺陷还可以进一步优化。如图 4.7 所示模式串 aaaab 在和主串 aaabaaaab 进行匹配时: s1p1等数字均为脚标
当 i4、j4 时s4跟p4(b≠a)失配若用之前的 next 数组则还需要进行 s4与p3、s4与p2、s4与p1这3次比较。事实上因为 、显然后面3次用一个和p4相同的字符跟s4比较毫无意义必然失配。那么问题出在哪里呢?
问题在于不应该出现 。理由是:当 时下次匹配必然是 跟比较,若 ,则相当于拿一个和相等的字符跟比较这必然导致继续失配这样的比较毫无意义。若出现则如何处理呢?
若出现 则需要再次递归将next [j] 修正为next [next [j] ]直至两者不相等为止更新后的数组命名为 nextval。计算 next 数组修正值的算法如下此时匹配算法不变。
void get_nextval(SString T,int nextval[]){int i1j0;nextval[1]0;while(iT.length){if(j0||T.ch[i]T.ch[j]){i; j;if(T.ch[i]!T.ch[j]) nextval[i]j;else nextval[i]nextval[j];}elsejnextval[j];}
}
KMP 算法对于初学者来说可能不太容易理解读者可以尝试多读几遍本章的内容并参考一些其他教材的相关内容来巩固这个知识点。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/924227.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!