4. 3
This presentation is the property of its rightful owner.
Sponsored Links
1 / 42

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


  • 61 Views
  • Uploaded on
  • Presentation posted in: General

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

Download Presentation

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

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.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


4297113

  • 4. 3 串的模式匹配算法(知识点三)

  • 子串定位运算又称为模式匹配或串匹配,此运算的应用非常广泛。例如,文本编辑程序中,经常要查找某一特定单词出现的位置。解此问题的有效算法能极大地提高文本编辑程序的响应性能。串的模式匹配定义:在主串中寻找子串在串中的位置。在模式匹配中,子串称为模式串,主串称为目标串。

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


4297113

  • 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)找出对应 的串


4297113

T串

defghijk

pos=4

Abcdefghijklmnopqrstuvw

S串

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


4297113

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


S ababcabcacbab t abcac

主串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


4297113

算法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).


4297113

算法4.5 Index(S,T,pos) 即模式匹配的时间复杂性分析

因为Index(S,T,pos)是一种有回溯的模式匹配算法 ;

所以,在最坏情况下的时间复杂度是O(n*m)。


4297113

  • 4. 3.2 模式匹配的一种改进算法

模式匹配的一种改进算法是D.E.Knuth 与V.R.Pratt和J.H.Morris同时发现的,因此,称该算法为克努特-莫里斯-普拉特算法(简称为KMP算法)。在串匹配算法中又称 KMP模式匹配算法。

KMP算法优点:可以在O(M+N)的时间复杂度内完成模式匹配操作,即对Index(S,T,pos)模式匹配算法的改进,取消了主串的回溯 。

KMP算法基本思想:每当匹配过程中出现字符比较不等时,i不回溯。


Kmp 1

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


Kmp 2

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


4297113

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


4297113

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

匹配成功


Kmp 11

KMP模式匹配算法小结-1

(1)结论: i可以不回溯,模式向右滑动到的新比较起点k ,并且k 仅与模式串T有关!

(2)需要讨论两个问题:

①如何由当前部分匹配结果确定模式向右滑动的新比较起点k?

②模式应该向右滑多远才是最高效率的?


4297113

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


Kmp 21

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指针不回溯)应与模式串中哪个字符再比较?


4297113

假设:这时应与模式串中第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个字符比较起继续进行。


4297113

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 }


4297113

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 字符之间的相关度越高,模式串向右滑动得越远,与主串进行比较的次数越少,时间复杂度就越低。


Kmp next j

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表示从模式串头部开始进行字符比较


4297113

KMP算法的时间复杂度可达到O(m+n)

其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。

在下面将要解决的问题是:如何求出下一个j的位置即next[j]=?,而不回溯i指针。


4297113

目前需要解决的问题是——

(1)若i不需回溯,则 j应该退回到何处?

(2)设退回到 next[j],

则next[j]=?

(3)对于给定的模式串,如何求解next[j]是问题的关键。

(4)next[j]与s串无关,只与t串有关


Next j

  • 0 当 j=1

  • max { k | 1< k < j且

  • “p1p2 …pk1 "=“pjk+1 pjk+2 …pj1 "}

  • (相同的前缀子串与后缀子串的最大长度+1)

  • 其他

Next [ j ]=

Next [ j ]函数定义和计算模式如下:


4297113

例:

  • 设有模式串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


4297113

算法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”


4297113

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;


4297113

KMP算法是在已知模式串的next函数值的基础上执行的,那么,如何求得模式串的next函数值呢? 从上述讨论可见:

k 的确定方法

当比较到模式第 j 个字符失配时,k ( next[j] )的值与模式的前 j 个字符有关,与目标无关。

此函数值仅取决于模式串本身而和相匹配的主串无关。我们可从分析其定义出发用递推的方法求得next函数值。


4297113

求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函数值问题看作是一个模式匹配问题,整个模式串既是主串又是模式串。而当前在匹配过程中,已经有:


4297113

求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)


4297113

  根据下边的图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。


4297113

下面再用图示法举例说明求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


4297113

求next函数值的过程是一个递推过程,分析如下:

已知: next[1] = 0;

假设: next[j] = k;

有 : ‘P1P2……Pk-1‘=‘Pj-k+1Pj-k+2……Pj-1‘

(2)若: Pj Pk

则: 需往前回朔,检查 Pj=P ?

这实际上也是一个匹配的过程;

不同在于:部分主串和模式串是同一个串


4297113

综上所述,根据分析结果式(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 要小得多,因此,对整个匹配算法来讲,所增加的这点时间是值得的。


4297113

还有一种特殊情况需要考虑:

例如:

S = aaabaaaab

T = aaaab

next[j]=01234

nextval[j]=00004


4297113

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


4297113

  • 4. 4 串操作应用举例

  • 4. 4.1 文本编辑

  • 4. 4.2 建立词索引表


4297113

  • 4. 4.1 文本编辑

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


4297113

将如下一段源程序中的每行语句的起始地址和长度信息存储在100开始的地址中;具体的源程序存放在201开始的内存中:

Main(){

float a,b,max;

scanf(“%f,%f”,&a,&b);

if a>b max=a;

else max=b;

}


4297113

201


4297113

4. 4.2 建立词索引表 p86

具体步骤:

(1)建立图书的书号及书名表的数据结构;

(2)根据图书名中的关键词,建立书号索引及对应的的数据结构;

(3)从图书文件中读入一个书目串;

(4)从目串中提取所有关键词插入词表;

(5)对词表的每一个关键词,在索引表中进行查找,并作相应的插入操作。

重复上述的(3)、(4)和(5)。

具体算法见P87-89。


4297113

第四章学习要点

1、 熟悉串基本操作的定义,并能利用这些基本操作来实现串的其它各种操作的方法。

2、 熟练掌握在串的定长顺序存储结构上实现串的各种操作的方法。

3、了解串的堆存储结构以及在其上实现串操作的基本方法。


4297113

4、理解串匹配的KMP算法,熟悉NEXT函数的定义,学会手工计算给定模式串的NEXT函数值和改进的NEXT函数值。

5、了解串操作的应用方法和特点。


4297113

第四章结束


  • Login