410 likes | 552 Views
第六章 字符串处理算法. 邹权(博士) 计算机科学系. 提要. 6.1 问题介绍 6.2 Z-box 算法 6.3 BM 算法 6.4 KMP 算法 6.5 关键字树 6.6 后缀树. 6.1 问题介绍. 问题提出:字符串精确匹配( Exact String Matching )问题可以描述如下:给定一个长字符串 T 和一个短字符串 P ,其中它们的长度分别是 |T|=m, |P|=n ,利用最理想的时间和空间找出全部的 T 中与 P 完全匹配的子串。 习惯的表示方法:字符串 T /P 中的第 i 个字符用 T(i) 来表示 。.
E N D
第六章 字符串处理算法 邹权(博士) 计算机科学系
提要 6.1 问题介绍 6.2 Z-box算法 6.3 BM算法 6.4 KMP算法 6.5 关键字树 6.6 后缀树
6.1 问题介绍 • 问题提出:字符串精确匹配(Exact String Matching)问题可以描述如下:给定一个长字符串T和一个短字符串P,其中它们的长度分别是|T|=m, |P|=n,利用最理想的时间和空间找出全部的T中与P完全匹配的子串。 • 习惯的表示方法:字符串T/P中的第i个字符用T(i)来表示。
6.2 Z-box算法 • [定义]Zi(S)表示在字符串S中从第i位起与S的前缀匹配的最长的字符串的长度。(数值) • 注:如果S在上下文中很明显,Zi(S)可以用Zi来表示。 • 例:S=a a b c a a b x a a z 那么 1 2 3 4 5 6 7 8 • Z5(S)=3; Z6=1;Z7= Z8=0
Z-box:其中每个与前缀匹配的子串就叫一个Z-box。对于任意数字i,如果S(i)属于某一个Z-box,则定义Li表示该Z-box的最左端的位置,Ri表示该Z-box的最右端的位置。Z-box:其中每个与前缀匹配的子串就叫一个Z-box。对于任意数字i,如果S(i)属于某一个Z-box,则定义Li表示该Z-box的最左端的位置,Ri表示该Z-box的最右端的位置。 • 例:在上例中L6=5 R6=7 • 性质:if i>j,then Ri>=Rj
[任务]在线性时间内(O|S|)计算S的所有Zi • [思想] Z2正常计算,然后归纳计算Zk,可以减少比较的次数
[思想]令S=P$T,其中$是特殊字符,在S和T中都不存在。利用Z算法计算Zi(S)。[思想]令S=P$T,其中$是特殊字符,在S和T中都不存在。利用Z算法计算Zi(S)。 • [方案]对于任意j,如果Zn+1+j(S)=n,则P出现在T(j)处。 • [加强]因为$的设定,对于任意i都有Zi<n,即k`总落在P内,所以当k>n时(即在T中)Zk值不用计算,只需要维持(maintain)R和L的值。
6.3 BM算法 • [名称] Boyer-Moore算法 • [特点]从左向右移动,自右向左扫描 • [技巧]坏字符规则 好后缀规则
6.3.1 坏字符规则 • [实质]把P中的坏字符x移到T中的x的下面 • [定义]对于字母表中的每一个字符x,R(x)表示x在P中的最右端的位置,如果P中没有x,则R(x)=0。 • [规则]对于P和T,如果P右面的n-i个字符都和T匹配,但是P(i) ≠T(k)。那么P可以向右移动max[1,i-R(T(k)]位。
1 2 3 4 5 6 7 8 9 0 • [例] T= x p b c t b x a b p q x P= t p a b x a b • 因为P(3)!=T(5),R(T(5))=R(t)=1 max[1,3- R(T(5))]=2 • 所以P可以向右移动2位,然后再从右向左依次同T比较
6.3.1 坏字符规则 • 普通坏字符规则有局限性: 1.不适合简单字母表 (DNA匹配) 2.不适合有大量重复子串的P (蛋白质匹配) • 加强规则:当P(i) ≠T(k)时,如果T(k)=x,将P中位置i左面最近的x移到T(k)下面。(更好的体现了坏字符规则的精神)。
相关讨论:普通的坏字符规则节省空间,它只需要O|∑|(∑是字母表)的空间存放R(x)(R(x)是一个数组);而加强坏字符规则可能需要更大的空间。相关讨论:普通的坏字符规则节省空间,它只需要O|∑|(∑是字母表)的空间存放R(x)(R(x)是一个数组);而加强坏字符规则可能需要更大的空间。 • 解决办法:可以不用二维数组,用一维数组,其中一维数组中的每个元素是一个数的序列。如:P=abacbabc 那么R(a)=6,3,1 所需空间O(n)
6.3.2 好后缀规则 • 坏字符规则的局限: 1.不适合小字母表 2.最差运行时间不是线性的 • 思想:看准T中的子串t,看P中还有没有子串匹配t • 区别:坏字符规则是去匹配T中的一个坏字符;好后缀规则是去匹配T中的一个子串
规则:给定P、T,T的子串匹配了P的一个后缀,但是再往左一个字符就不匹配了。然后寻找t`:规则:给定P、T,T的子串匹配了P的一个后缀,但是再往左一个字符就不匹配了。然后寻找t`: 1. t`和t相同 2. t`不是P的后缀 3. t`左面的那个字符同与t匹配的P的后缀的左面那个字符不相同。
将P向右移,直到t`位于t的下面。 • 如果t`不存在,则将P向右移动最少的格数使:P的前缀同t的后缀相匹配。 • 如果这种情况也不存在,那么将P向后移动n位。 • 如果T中发现了一个P,将P向右移动最少格数使:P的前缀能够和T中发现的P的后缀相匹配。如果没有这种匹配,则将P向后移动n位。
例 1 2 3 4 5 6 7 8 9 0 1 2 3 4 T p r s t a b s t u b a b v q x r P q c a b d a b d a b 1 2 3 4 5 6 7 8 9 0 • 因为P(8)!=T(10),t=ab,又因为t`在P中开始的位置是3,所以P向右移动6步 • [对比]在本例中,坏字符规则只能使P向右移动1步
6.4 KMP算法 • [名称] Knuth-Morris-Pratt算法 • [特点]从左向右移动,从左向右比较
[定义]对于P中的每个位置i, spi(P)为P[1...i]中最长后缀的长度,该后缀与P的一个前缀匹配[数值] • [注]对于任意串sp1=0 • [例] 1 2 3 4 5 6 7 8 9 0 1 P= a b c a e a b c a b d sp2=sp3=0; sp4=1; sp8=3; sp10=2 • [定义]对于P中的每个位置i ,spi`(P)为P[1...i]中最长后缀的长度,该后缀与P的一个前缀匹配,并且P(i+1)≠P(sp`i+1) • [例]上例中sp`8=0, sp`9=4
[转移规则]对于P和T,如果P(i+1)≠T(k),则将P[1…sp`i]移到T[k- sp`i...k-1]下面,也就是将P向右移了i- sp`i步,然后比较P(sp`i+1)和T(k);如果发现了一个P在T中,将P向右移动n-sp`n步 • [优点]移动之后,前sp`个字符不用再比较了,直接比较P(sp`i+1)和T(k)就可以了 • [定理]在Knuth-Morris-Pratt算法中,字符比较的次数最多是2m
[real-time]对于T的任意位置,如果第一次检测它,也就是最后一次检测它(就是说对每一位最多只比较一次),则说它是real-time.[real-time]对于T的任意位置,如果第一次检测它,也就是最后一次检测它(就是说对每一位最多只比较一次),则说它是real-time. • [意义]1. 对于一个小内存机器,在下一个字符读入之前,要保证上一个字符已经处理完毕。 2. 在确定的实例中,real-time方法应该运行的更快一些。
Knuth-Morris-Pratt算法不是real-time的。因为对于不匹配的位置,还至少要再比较一次。Knuth-Morris-Pratt算法不是real-time的。因为对于不匹配的位置,还至少要再比较一次。 • 如何 把Knuth-Morris-Pratt算法转变成一个real-time方法?
[定义]x是字母表中的一个字符,sp`(i,x)(P)=sp`i(P)并且P(sp`i+1)=x[定义]x是字母表中的一个字符,sp`(i,x)(P)=sp`i(P)并且P(sp`i+1)=x • [思想]P同T进行比较,T(k)=x处与P(i+1)不匹配,那么P向右移动i- sp`(i,x)步。这样T(k)=P(sp`(i,x)+1)=x不用比较,直接比较P(sp`(i,x)+2)和T(k+1)即可。显然这种方法是real-time的.
6.5 关键字树 • [目标]在一个目标序列中搜索多个查询序列的完全匹配 • [算法] Aho-Corasick算法 • [数据结构]关键字树
[定义]集合P的关键字树(keyword tree)是一棵有根树,它的根记为K,满足 1. 每一条边都标定一个字母 2. 从同一结点分开的任意两条边对应着不同的字母 3. 每一个字符串Pi都对应着一个结点v,从树根K出发到达v的路径可以恰好正确地拼出字符串Pi,并且树K的每一个叶结点都对应P的某一个模式。
很容易看出构造一颗关键字树的时间复杂性是O(n)。很容易看出构造一颗关键字树的时间复杂性是O(n)。 • 关键字树建立起来以后,对于普通的搜索方法时间复杂性是O(mb),其中b=max|Pi|。然而,我们可以利用失效链接(failure link)技术来加速搜索过程。 • 失效链接的前提是在P中不存在一个模式是另一个模式的子串!
定义1:L(v)表示从根结点到结点v的字符连接起来得到的字符串。定义1:L(v)表示从根结点到结点v的字符连接起来得到的字符串。 • 定义2:lp(v)表示L(v)中最长后缀的长度,该后缀是P中某个模式的前缀。(数值) L(v)=potat lp(v)=|tat|=3
定义3 :α表示字符串L(v)的长为lp(v)的后缀,那么在关键字树中有唯一的结点标定α。该唯一的结点记为nv。当lp(v)=0时,nv就是跟节点K。 • 定义4:有序对(v,nv)称为一个失效链接.
失效链接的建立算法的关键是在线性时间内得到所有的失效链接,可以采用归纳的思想:若v是根r,或v是r的儿子,则nv=r;假设距根结点的距离小于等于k的所有的结点v的nv都已经计算出来,我们现在计算据r的距离为k+1的结点v的nv.失效链接的建立算法的关键是在线性时间内得到所有的失效链接,可以采用归纳的思想:若v是根r,或v是r的儿子,则nv=r;假设距根结点的距离小于等于k的所有的结点v的nv都已经计算出来,我们现在计算据r的距离为k+1的结点v的nv.
假设所求的结点为v(距r为k+1的结点),他的父亲是v’,v’到v这条边所标定的字母是x,假设所求的结点为v(距r为k+1的结点),他的父亲是v’,v’到v这条边所标定的字母是x, ①若nv’的下一个字符有x,则设该边的另一个结点为w’,即有nv=w’ ②若nv’到它所有儿子的边所标定的字母均不是x,则字符串L(nv)(表示从根结点到nv所标定的字符串)是L(nv’)的一个后缀跟上x,又因为该后缀与根结点开始的字符串匹配(类似前缀)所以可以检测 后面是否有x,如果没有则继续下去。
6.6 后缀树 给定长度为n的字符串S, S的后缀树是一颗有n个叶子结点的树。树中的每一条边上都标识着字符串S的一个非空子串。由根结点开始到任何一个叶子结点的路径上所有的标识连接起来构成的字符串对应着字符串S的某一个后缀。由同一结点发出的任何两条边上所标识的字符串的首字符不允许相同。
构造后缀树所需要的时间和空间都是线性于字符串长度的,这也是后缀树得到广泛应用的原因之一。经典的后缀树构造算法有两种,分别是MCC算法和UKK算法。
6.6.1 后缀树应用——字符串精确匹配 • P和T同时已知:利用后缀树和BM算法是等效的。 • P已知、固定,T不固定:利用BM算法或KMP算法即可,后缀树叶可以在同样的时间内完成。
T已知、固定,P不固定:可以先对T进行预处理,然后每输入一个P利用后缀树在O(n+k)时间内找出所有的P(其中|P|=n,k是P在T中出现的次数)。这也是后缀树优于其他算法之所在。T已知、固定,P不固定:可以先对T进行预处理,然后每输入一个P利用后缀树在O(n+k)时间内找出所有的P(其中|P|=n,k是P在T中出现的次数)。这也是后缀树优于其他算法之所在。 • 这类问题称为子串问题,一个典型的应用是在DNA数据库中,比如输入一段DNA序列,我们想知道该序列在我们的DNA数据库中是否已经存在。
6.6.2后缀树应用——最长公共子串 • 将S1和S2建立到一颗后缀树中,这种融入了多个字符串的后缀树称为广义后缀树。具体思想如下:对于每一个叶节点,如果它标定的字符串(从根节点到该节节点的路径上的字符串)是S1的后缀,则给他一个标记flag=1;如果一个叶节点标定的字符串是S2的后缀则它的标记flag=2。那么同时具有两种叶节点为后代的内部节点所标定的字符串就是公共子串。那么找出最深的这样的内部节点即可。
后缀树的缺点 • 不容易on line • 不容易处理大规模数据
字符串(String)=序列(Sequence) • 子字符串VS子序列 • 最长公共子串VS最长公共子序列(LCS) • 后缀树VS动态规划
小结 [Z算法]线性时间解决问题 [Knuth-Morris-Pratt算法] 1.可以扩展到Aho-Corasick算法,用来在O(m)时间内解决搜索P的集合。 2.当T是在线输入的(on-line)具有很好的扩展性,可以加强到real-time [Boyer-Moore算法]最差搜索时间是线性的,期望搜索时间是亚线性的(sublinear time) [后缀树算法] 1.预处理T,而不是P,搜索时间是O(n)的 2.解决一些更复杂问题,其中相当一部分是前面几个算法无法处理的