1 / 42

这是串的一种重要操作,很多软件,若有 “ 编辑 ” 菜单项的话,则其中必有 “ 查找 ” 子菜单项。

4. 3 串的模式匹配算法 ( 知识点三 ). 子串定位运算又称为模式匹配或串匹配,此运算的应用非常广泛。例如,文本编辑程序中,经常要查找某一特定单词出现的位置。解此问题的有效算法能极大地提高文本编辑程序的响应性能。 串的模式匹配定义 : 在主串中寻找子串在串中的位置。在模式匹配中,子串称为 模式串 ,主 串称为 目标串 。. 这是串的一种重要操作,很多软件,若有 “ 编辑 ” 菜单项的话,则其中必有 “ 查找 ” 子菜单项。. 4. 3.1 求子串位置的定位函数 Index(S,T,pos).

kimama
Télécharger la présentation

这是串的一种重要操作,很多软件,若有 “ 编辑 ” 菜单项的话,则其中必有 “ 查找 ” 子菜单项。

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 4. 3 串的模式匹配算法(知识点三) • 子串定位运算又称为模式匹配或串匹配,此运算的应用非常广泛。例如,文本编辑程序中,经常要查找某一特定单词出现的位置。解此问题的有效算法能极大地提高文本编辑程序的响应性能。串的模式匹配定义:在主串中寻找子串在串中的位置。在模式匹配中,子串称为模式串,主串称为目标串。 这是串的一种重要操作,很多软件,若有“编辑”菜单项的话,则其中必有“查找”子菜单项。

  2. 4. 3.1 求子串位置的定位函数 Index(S,T,pos) Index(S,T,pos)称为模式匹配(S为主串, T为模式串); 初始条件:S和T存在,T是非空串, 1 <= pos <= (S的长度)。 操作结果:若主串S中存在和串T相同的(模式串)子串,则返回它在主串S中第pos个字符之后第一次出现的位置;否则返回0。 例如:对某文本进行编辑时,可以运用如下步骤: (1)编辑;(2)查找 ——“输入查找文本(字符串)”;(3)找出对应 的串

  3. T串 defghijk pos=4 Abcdefghijklmnopqrstuvw S串 按照上述主串S和子串T求子串位置的定位函数 Index(S,T,pos)的返回值是4

  4. p79算法4.5 int Index (SString S, SString T, int pos) { // 返回子串T在主串S中第pos个字符之后的位置。若不存在, // 则函数值为0。其中,T非空,1≤pos≤StrLength(S)。 i = pos; j = 1; while (i <= S[0] && j <= T[0]) { if (S[i] == T[j]) { ++i; ++j;}// 继续比较后继字符 else{i = i-j+2; j = 1;}// 指针后退重新开始匹配 } if (j > T[0]) return i-T[0]; else return 0; } // Index

  5. 主串s="ababcabcacbab",模式T="abcac" 主串s 模式串T ="abcac" 模式串T ="abcac" 模式串T ="abcac" 核心语句 i=11 i=6 b if (S[i] == T[j]) { ++i; ++j; }// 继续比较后继字符 else { i = i-j+2; j = 1; } 返回值为11-5=6 因为T[0]=5 J=6

  6. 算法4.5 Index(S,T,pos) 即模式匹配的特点: 特点一:Index(S,T,pos) 算法的匹配方法简单,理解方便,适合一些文本编辑,效率较高; 特点二:Index(S,T,pos) 算法的匹配方法简单,理解方便,适合一些文本编辑,效率较高;正常情况下,时间复杂度为O(M+N); 特点 三:如果主串和子串存在多个零时,如: S=‘0000…1’{总共52个零};T=‘00000001’, 则出现多次重复的比较,即出现不等时, I 指针每次都回朔到i-1位置,这样浪费了大量的比较时间,整个匹配需要回朔45次,While循环语句的执行次数为46*8(index*m).

  7. 算法4.5 Index(S,T,pos) 即模式匹配的时间复杂性分析 因为Index(S,T,pos)是一种有回溯的模式匹配算法 ; 所以,在最坏情况下的时间复杂度是O(n*m)。

  8. 4. 3.2 模式匹配的一种改进算法 模式匹配的一种改进算法是D.E.Knuth 与V.R.Pratt和J.H.Morris同时发现的,因此,称该算法为克努特-莫里斯-普拉特算法(简称为KMP算法)。在串匹配算法中又称 KMP模式匹配算法。 KMP算法优点:可以在O(M+N)的时间复杂度内完成模式匹配操作,即对Index(S,T,pos)模式匹配算法的改进,取消了主串的回溯 。 KMP算法基本思想:每当匹配过程中出现字符比较不等时,i不回溯。

  9. a b a b c a b c a c b a b a b a b c a b c a c b a b KMP模式匹配算法图示-1 i 第 1 趟 i=3,j=3时,失败即 s3 ≠ t3;; S1=t1 ; s2=t2; 因为t1≠t2;所以t1≠s2 a b c a c j 第 3 趟 a b c a c

  10. i a b a b c a b c a c b a b a b a b c a b c a c b a b 第 4 趟 j a b c a c KMP模式匹配算法图示-2 第 3 趟 a b c a c i=7,j=5失败s4=t2;t1≠t2 ∴t1≠s4

  11. i a b a b c a b c a c b a b a b a b c a b c a c b a b 第 5 趟 j a b c a c KMP模式匹配算法图示-3 第 3 趟 a b c a c i=7,j=5失败s5=t3;t1≠t3 ∴t1≠s5

  12. i a b a b c a b c a c b a b a b a b c a b c a c b a b 第 6 趟 j a b c a c KMP模式匹配算法图示-4 第 3 趟 a b c a c i=7,j=5失败s5=t3;t1≠t3 ∴t1≠s5 匹配成功

  13. KMP模式匹配算法小结-1 (1)结论: i可以不回溯,模式向右滑动到的新比较起点k ,并且k 仅与模式串T有关! (2)需要讨论两个问题: ①如何由当前部分匹配结果确定模式向右滑动的新比较起点k? ②模式应该向右滑多远才是最高效率的?

  14. i j S="a b a b caa bc c b a b" (1)模式滑动到第k个字符,有p1~pk-1=Si-(k-1)~Si-1 (2)再观察失配时,有pj-(k-1) ~pj-1 =Si-(k-1) ~Si-1 两式联立可得:p1~pk-1=pj-(k-1)~pj-1 Si-(k-1)... si-1 当 si≠ pj失匹时 p="a bc a a ba b c" P1 --pk-1 Pj-(k-1) Pj-1

  15. i i j k S="a b a b caa bc c b a b" S="a b a b ca abc c b a b" p="a bc a c" KMP模式匹配算法小结-2 关注部分匹配时的重要特征—— p="a bc a a b a b c" 假设:主串为’S1S2,…,Sn’ 模式串为’p1,p2,…,pn’ 当匹配过程中,产生失配(即si<> pj)时,模式串“向右滑动多远?”即主串中第i个字符(i指针不回溯)应与模式串中哪个字符再比较?

  16. 假设:这时应与模式串中第k个(k<j) 个字符继续比较,则模式串中第k-1个字符的子串必须满足下列关系式(教材P81的4-2),且不可能存在k’>k满足下列关系式(4-2): 模式滑动到第k个字符,有p1~pk-1=Si-(k-1)~Si-1…(4-2) 而已经得到的“部分匹配”的结果是: pj-(k-1) ~pj-1 =Si-(k-1) ~Si-1 …(4-3) 将(4-2) 和(4-3)两式联立可得:p1~pk-1=pj-(k-1)~pj-1 …(4-4) 反之,若模式串中存在满足式(4-4)的两个子串,则当匹配过程中,主串第I 个字符与模式串中第j 个字符比较不等时,仅需将模式向右滑动至模式中第k个字符和主串中第i个字符对齐,这时,模式串中k-1个字符‘p1~pk-1 ’必定与主串中第i个字符之前的k-1的子串‘Si-(k-1)~Si-1 ’ 相等,因此,匹配仅需从第k个字符与主串中第i个字符比较起继续进行。

  17. p1…pk-1=pj-(k-1) …pj-1的物理意义是什么? 模式应该向右滑多远才是最高效率的? KMP模式匹配算法小结-3 p1…pk-1=pj-(k-1) …pj-1说明了什么? (1) k与j具有函数关系,由当前失配位置 j ,可以计算出滑动位置 k(即比较的新起点); (2)滑动位置k仅与模式串T有关。 从第1位往右 经过k-1位 从j-1位往左 经过k-1位 k=max { k |1<k<j且p1…pk-1=pj-(k-1) …pj-1 }

  18. KMP模式匹配算法小结-4 令k = next[ j ],则: 0 当j=1时 //不比较 max { k | 1<k<j 且p1…pk-1=pj-(k-1) …pj-1 } 1 其他情况 next[ j ]= next[j]函数表征着模式T中最大相同首子串和尾子串(真子串)的长度。 可见,模式中相似部分越多,则next[j]函数越大,它既表示模式 T 字符之间的相关度越高,模式串向右滑动得越远,与主串进行比较的次数越少,时间复杂度就越低。

  19. KMP模式匹配算法中next[j]的计算方法 • 计算next[j]的方法: • 当j=1时,next[j]=0;//next[j]=0表示根本不进行字符比较 • 当j>1时,next[j]的值为:模式串的位置从1到j-1构成的串中所出现的首尾相同的子串的最大长度加1。 • 当无首尾相同的子串时next[j]的值为1。next[j]=1表示从模式串头部开始进行字符比较

  20. KMP算法的时间复杂度可达到O(m+n) 其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。 在下面将要解决的问题是:如何求出下一个j的位置即next[j]=?,而不回溯i指针。

  21. 目前需要解决的问题是—— (1)若i不需回溯,则 j应该退回到何处? (2)设退回到 next[j], 则next[j]=? (3)对于给定的模式串,如何求解next[j]是问题的关键。 (4)next[j]与s串无关,只与t串有关

  22. 0 当 j=1 • max { k | 1< k < j且 • “p1p2 …pk1 "=“pjk+1 pjk+2 …pj1 "} • (相同的前缀子串与后缀子串的最大长度+1) • 其他 Next [ j ]= Next [ j ]函数定义和计算模式如下:

  23. 例: • 设有模式串T=“abaabcac“, 计算next[j] j 1 2 3 4 5 6 7 8 模式串 a b a a b c a c next[j] 0 1 1 2 2 3 1 2

  24. 算法4.6 P82 int Index_KMP(SString S, SString T, int pos) { // 1≤pos≤StrLength(S) i = pos; j = 1; while (i <= S[0] && j <= T[0]) { if (j == 0 || S[i] == T[j]) { ++i; ++j; } // 继续比较后继字符 else j = next[j]; // 模式串向右移动 } if (j > T[0]) return i-T[0]; // 匹配成功 else return 0; } // Index_KMP 主串S=“acabaabaabcacaabc” 模式串T=“abaabc”

  25. begin i = pos; j = 1; N N 返回0 i <= S[0] && j <= T[0])? (j > T[0])? y y return i-T[0] N (j == 0 || S[i] == T[j])? j = next[j] end y i=i+1 ; j=j+1;

  26. KMP算法是在已知模式串的next函数值的基础上执行的,那么,如何求得模式串的next函数值呢? 从上述讨论可见: k 的确定方法 当比较到模式第 j 个字符失配时,k ( next[j] )的值与模式的前 j 个字符有关,与目标无关。 此函数值仅取决于模式串本身而和相匹配的主串无关。我们可从分析其定义出发用递推的方法求得next函数值。

  27. 求next函数值的过程是一个递推过程,分析如下-1:求next函数值的过程是一个递推过程,分析如下-1: 已知: next[1] = 0;    (4-6) 假设: next[j] = k;      有 : ‘P1P2……Pk-1‘=‘Pj-k+1Pj-k+2……Pj-1’(4-7) 其中k为满足1<k<j的某个值,且不可能存在k’>k满足等式(4-7)。这时, next[j] = ?可能存在两种情况: 情况一: 若:Pk= Pj,则表明在模式串中存在, ‘P1P2……Pk ‘=‘Pj-k+1Pj-k+2……Pj ’(4-8) 且不可能存在k’>k满足等式(4-8),也就是说next[j+1] = k+ 1,即 next[j+1] = next[j] + 1 = k + 1 情况二: Pk<> Pj , 则表明在模式串中      ‘P1P2……Pk ’ < > ‘Pj-k+1Pj-k+2……Pj ’ 这时,可将求next函数值问题看作是一个模式匹配问题,整个模式串既是主串又是模式串。而当前在匹配过程中,已经有:

  28. 求next函数值的过程是一个递推过程,分析如下-2:求next函数值的过程是一个递推过程,分析如下-2: 有 : ‘Pj-k-1‘=‘P1’, Pj-k-2= P2,‘Pj-1 ‘=‘Pk’ 则当Pk<> Pj时, 应将模式向右滑动至模式中的第next[k]个字符和主串中第j 个字符相比较。若next[k]=k’,且pj=pk’,则说明在主串中第j+1个字符之前存在一个长度为k’ (即next[k]最长子串,和模式串中从首字符起长度为k’的子串相等,即(P83) 有 : ‘P1P2……Pk’‘=‘Pj-k’…,Pj’(1<k’<k<j)(4-10) 就是说, next[j+1]=k’+1(4-11) 同理,若Pj<> Pk’,则将模式继续向右滑动直至将模式next[k’] 个字符和pj对齐,…,以此类推,直至pj和模式中某个字符匹配成功或者不存在任何k’(1<k’<j)满足等式(4-10) ,则 next [j+1] = 1(4-12)

  29.   根据下边的图4.6 中的模式串,已经求出前 6个字符的next函数值,现求next[7]=?, 因为next[6]=3,又因p6<>p3, 则需比较p6和p1(因为next[3]=1,这相当于将子串模式向右滑动, 由于p6<>p1 ,而且next[1]=0,所以,next[7]=1,而p7=p1 ,则next[8]=2。

  30. 下面再用图示法举例说明求next函数值的匹配过程:下面再用图示法举例说明求next函数值的匹配过程: 设 next[j]=k j=5,k=2 则 next[5]=2 P1 …Pk-1 =Pj-k+1 …Pj-1 P1 =P4 若 (1)Pj =Pk P2 =P5 有 P1 P2 =P4 P5 (P1 …Pk-1 =Pj-k+1 …Pj-1) j-1=5 ∴j=6 k-1=2 next[6]=3 next[j+1]= next[j]+1 = k+1 模式串的匹配过程 j=5 j+1=6 目标aba abc a c 模式 a b a 0 1 1 2 2 3 J=5 J+1=6

  31. 求next函数值的过程是一个递推过程,分析如下:求next函数值的过程是一个递推过程,分析如下: 已知: next[1] = 0; 假设: next[j] = k; 有 : ‘P1P2……Pk-1‘=‘Pj-k+1Pj-k+2……Pj-1‘ (2)若: Pj Pk 则: 需往前回朔,检查 Pj=P ? 这实际上也是一个匹配的过程; 不同在于:部分主串和模式串是同一个串

  32. 综上所述,根据分析结果式(4-6)、(4-9)、(4-11)和(4-12),仿照KMP算法,可以求出next函数值的算法,如算法4.7(p83)所示:综上所述,根据分析结果式(4-6)、(4-9)、(4-11)和(4-12),仿照KMP算法,可以求出next函数值的算法,如算法4.7(p83)所示: void get_next(SString &T, int &next[] ) { // 求模式串T的next函数值并存入数组next i = 1; next[1] = 0; j = 0; while(i < T[0]){ if (j = 0 || T[i] == T[j]) {++i; ++j; next[i] = j; } else j = next[j];} } // get_next 算法4.7的时间复杂度为 O(m),通常模式串的长度要比主串的长度n 要小得多,因此,对整个匹配算法来讲,所增加的这点时间是值得的。

  33. 还有一种特殊情况需要考虑: 例如: S = aaabaaaab T = aaaab next[j]=01234 nextval[j]=00004

  34. void get_nextval(SString &T, int &nextval[]) { i = 1; nextval[1] = 0; j = 0; while (i < T[0]) { if (j = 0 || T[i] == T[j]) { ++i; ++j; if (T[i] != T[j]) next[i] = j; else nextval[i] = nextval[j]; } else j = nextval[j]; } } // get_nextval

  35. 4. 4 串操作应用举例 • 4. 4.1 文本编辑 • 4. 4.2 建立词索引表

  36. 4. 4.1 文本编辑 为了管理文本串中的页和行,在文本编辑时,编辑软件先为文本串建立相应的页表和行表,页表的每一项列出页号和该页的起始行号,行表的每一项则指示每一行的行号、起始地址和该行子串的长度。行表和页表与串是分开存储的。

  37. 将如下一段源程序中的每行语句的起始地址和长度信息存储在100开始的地址中;具体的源程序存放在201开始的内存中:将如下一段源程序中的每行语句的起始地址和长度信息存储在100开始的地址中;具体的源程序存放在201开始的内存中: Main(){ float a,b,max; scanf(“%f,%f”,&a,&b); if a>b max=a; else max=b; }

  38. 201

  39. 4. 4.2 建立词索引表 p86 具体步骤: (1)建立图书的书号及书名表的数据结构; (2)根据图书名中的关键词,建立书号索引及对应的的数据结构; (3)从图书文件中读入一个书目串; (4)从目串中提取所有关键词插入词表; (5)对词表的每一个关键词,在索引表中进行查找,并作相应的插入操作。 重复上述的(3)、(4)和(5)。 具体算法见P87-89。

  40. 第四章学习要点 1、 熟悉串基本操作的定义,并能利用这些基本操作来实现串的其它各种操作的方法。 2、 熟练掌握在串的定长顺序存储结构上实现串的各种操作的方法。 3、了解串的堆存储结构以及在其上实现串操作的基本方法。

  41. 4、理解串匹配的KMP算法,熟悉NEXT函数的定义,学会手工计算给定模式串的NEXT函数值和改进的NEXT函数值。4、理解串匹配的KMP算法,熟悉NEXT函数的定义,学会手工计算给定模式串的NEXT函数值和改进的NEXT函数值。 5、了解串操作的应用方法和特点。

  42. 第四章结束

More Related