收藏 分享(赏)

正则表达式的DFA算法.doc

上传人:wspkg9802 文档编号:4563584 上传时间:2019-01-02 格式:DOC 页数:17 大小:3.48MB
下载 相关 举报
正则表达式的DFA算法.doc_第1页
第1页 / 共17页
正则表达式的DFA算法.doc_第2页
第2页 / 共17页
正则表达式的DFA算法.doc_第3页
第3页 / 共17页
正则表达式的DFA算法.doc_第4页
第4页 / 共17页
正则表达式的DFA算法.doc_第5页
第5页 / 共17页
点击查看更多>>
资源描述

1、正则表达式1 正则表达式的定义正则表达式(Regular Expression)是一种强大的,便捷的,高效的文本处理工具,它可以表示比单字符、字符串集合等更加复杂的搜索模式。下面首先给出正则表达式和它所表达语言的形式化定义。一个正则表达式 RE 是符号集合 , |, *, (, ) 上的一个字符串,它可以递归定义如下:空字符 是正则表达式。任意字符 是正则表达式。如果 RE1和 RE2都是正则表达式,则(RE 1),(RE1RE2),(RE1 | RE2)和(RE 1*)亦是正则表达式。通常(RE 1RE2)可以简写为 RE1RE2。符号“”, “*”, “|”称为操作符,可以通过为每个操作符

2、赋予优先级来消除更多的括号。为了方便起见,这里使用了额外的后缀操作符“+”,它的含义是 RE+=RERE*。其他所使用的操作符如”?” ,字符组, ”.”等实际上都可以用上面的方式来表达。下面定义正则表达式所表达的语言。正则表达式 RE 所表达的语言是 上的一个字符串集合。根据 RE 的结构,可以将它递归的定义如下:如果 RE 是 ,则 L(RE)=,即空串。如果 RE 是 ,则 L(RE)=,即包含一个字符的 单串。如果 RE 是(RE 1)这种形式,则 L(RE) = L(RE1)。如果 RE 是(RE 1RE2)这种形式,则 L(RE) = L(RE1) L(RE2),其中 W1W2可以

3、看成是字符串集 w 的集合,其中,w = w1w2并且 w1W1,w2W2。操作符表示字符串的连接。如果 RE 是(RE 1|RE2)这种形式, 则 L(RE) = L(RE1)L(RE2),是这两种语言的并集, “|”称为并操作符。如果 RE 是(RE 1*)这种形式,则 L(RE) = L(RE)* = i 0L(RE)i,其中 L0=并且 Li = LLi-1,它表示字符串集合是由 0 个或者多个 RE1表达的字符串 连接而成。 “*“称为星操作符。正则表达式 RE 的规模是指它所包含的属于字母表 的字符的个数,在算法复杂性分析中,它是一个重要的度量。在文本 T 中搜索正则表达式 RE

4、的问题就是找到文本中所有属于语言 L(RE)的字串。搜索的方法是首先将正则表达式解析成一颗表达式树,然后将表达式树转换成非确定性有限自动机(NFA ) 。直接使用 NFA 进行搜索是可行的,然而 NFA 算法处理速度通常比较慢,一般的,搜索过程最坏情况时间复杂度是 O(mn),但是所需存储空间并不多。另外一种策略是将 NFA 转变成确定性有限自动机(DFA ) ,它的搜索时间是 O(n),但是构造这样的一个自动机所需的最坏情况时间和空间复杂度都是 O(2m)。2 构造解析树通常来说,解析树并不是唯一的。在解析树中,每个叶节点都是使用 中的一个字符来标识的,而每个中间节点则使用操作符集合|, ,

5、 * 中的一个进行标识。一种可能的解析树使用二叉树来表示,二叉树的父节点是一个操作符,两个子节点表示这个操作符作用的两个子表达式。如正则表达式(AT|GA)(AG|AAA)*)的解析树可以表示如下:。TAAG|GAA|*AA程序中使用这个二叉树的后序遍历序列来存储这个解析树,那么上面那个正则表达式的存储序列如下:A T G A | A G A A A | * 。函数 re2post 就是将输入的正则表达式字符串转换成解析树的后序遍历序列。解析过程中有两个重要的变量,natom 和 nalt,natom 表示解析到这个字符为止,已经有多少个原子结构,而 nalt 表示解析到这个字符为止,已经有多

6、少个分支结构。正则表达式中的括号表示一个子表达式,这个子表达式对于括号外面的表达式来说是一个原子结构,它内部的natom 和 nalt 的值和外部的表达式的这些值没有关系。为了正确的处理这种括号及其嵌套,程序中使用堆栈来辅助解析,每当碰见“(“,将当前的 natom 和 nalt 压入栈中,新的natom 和 nalt 从零开始;而解析到” ) “时,则根据当前的 natom 和 nalt 值进行后续处理,然后从栈中弹出上一层的 natom 和 nalt。具体的处理算法如下:Parse( p = p1p2pm, last)v = 0;While plast $ DoIf plast ThenI

7、f (natom 1) Then-natom; v v+.;EndIfv v + plast; natom+;Else If = |v v + (natom-1).nalt+;Else If = * or + or ?v v + p last;Else If = (If (natom 1) Then-natom; v v+.;EndIfpush(natom, nalt);nalt 0; natom 0;Else If = )v v + (natom-1).v v + nalt|pop(natom, nalt);natom+;EndIfEndwhilev v + (natom-1) .v v +

8、 nalt|Return v;3 构造 NFA有多种方式用来从正则表达式构造 NFA,最常见的两种,也是实践中经常使用的是Thompson 构造法和 Glushkov 构造法。Thompson 方法简单,并且构造的 NFA 中状态数量(最多 2m 个)和转移数量(最多 4m 个)都是线性的。这种自动机存在 -转移,即空转移。Thompson 自动机Thompson 自动机构造的核心思想是先形成正则表达式 RE 对应的树表示 Tre,然后自底向上地对树的每个节点 v,构造一个自动机 Th(v)来识别以 v 为根的子树所表达的语言。根据不同类型的中间节点和叶节点,有不同的自动机构造方法,具体情况如

9、下。空字的构造方法。自动机由连接两个节点而组成I F单字符 的构造方法,与空字类似,只不过转移是使用字符来标识,而不是使用空字符串。I F相连节点的构造法。将两个子节点 vl 和 vr 对应的 Thompson 自动机合并,即第一个自动机的终止状态成为第二个自动机的初始状态。vlvrI F联合节点的构造法。对于联合节点,则必须通过子节点对应的自动机 Th(vl)和 Th(vr)中的一个。这时需要 -转移。构造过程中,必须添加两个新的状态:一个是初始状态 I,从它有两个 -转移分别到自动机 Th(vl)和 Th(vr)的初始状态;另一个是终止状态 F,从自动机Th(vl)和 Th(vr)的终止状

10、态分别由 -转移到达终止状态 F。它表达的语言是 REvl|REvr。vlvrI F星节点的构造方法,它使用了同联合节点构造方法相同的思想。首先,因为对于语言REv*,节点 v 的唯一子节点 v*可以被重复任意多次,所以需要创建一个从自动机 Th(v*)的终止状态指向其初始状态的 -转移。但是星符号也意味着自动机 Th(v*)可以被忽略。因此需要创建初始节点 I 和终止节点 F,并用一个 -转移把它们连接起来。另外,再创建两条-转移分别用来从节点 I 指向 Th(v*)的初始状态以及从 Th(v*)的终止状态指向 F。最终,自动机识别的语言是(RE v*)* 。v*I F 整个 Thompso

11、n 算法包含自底向上的树的遍历,同时保证根节点开始构造的自动机即为能够表示整个正则表达式的 Thompson 自动机。在构造树表示中的每一个节点时,自动机中相应地最多增加 2 个状态和 4 个转移。因此,构造完成后,状态与转移的数量最多为 2m 和 4m 个。下面图表示了正则表达式(AT|GA)(AG|AAA)*)的构造过程。I FA TI FG AI FA TGAI FA GI FA A AIFA GAA AIA GAA AF1 2 34 5 60A TGA9 1 0 1 11 2 1 3 1 57A GAA1 4A81 71 6Thompson 算法由函数 post2nfa 实现。post

12、2nfa 函数输入一个数组表示的解析树,返回Thompson 自动机,它使用了下面两种数据结构。typedef struct _stateint c; 表示状态的特性,小于 256 时,表示此状态输入 c 将转移到 out 指向的状态struct _state *out; 下一个状态struct _state *out1; c 是 Split 时,指向下一个分支状态int lastlist;int stateid; 状态编号 State;typedef union _ptrlistunion _ptrlist *next;State *s; Ptrlist;typedef struct _fr

13、agState *start;Ptrlist *out; Frag; Frag 结构是一个部分 NFA,start 指向 NFA 的开始状态, out 指向一系列位置,这些位置需要被设置成这个部分 NFA 的下一个状态。函数中使用了一个 Frag 的栈来保存 NFA 的片段。stackp = stack;for(p=postfix; *p; p+)switch(*p)default: 单字符的构造/* 生成一个新的状态 s */s = state(g, *p, NULL, NULL);/* 生成一个新的 Frag,用 s 作为 start 状态,它的 out 作为终止状态,压入栈中 */pus

14、h(frag(s, list1(break;case .+256: 相连节点的构造e2 = pop();e1 = pop();/* 连接两个相邻 Frag,第一个的 out 指向第二个的 start 状态 */patch(e1.out, e2.start);/* 生成一个新的 Frag,用 e1 的 start 状态作为 start 状态, e2 的 out 作为终止状态,压入栈中 */push(frag(e1.start, e2.out);break;case |+256: 联合节点的构造e2 = pop();e1 = pop();/* 生成一个新的 Split 状态 s,指向 e1 和 e

15、2 的 start 状态*/s = state(g, Split, e1.start, e2.start);/* 生成一个新的 Frag,用 Split 的 start 状态作为 start 状态,终止状态为 e1 和 e2的终止状态的连接,压入栈中 */push(frag(s, append(e1.out, e2.out);break;case ?+256: 问号节点的构造e = pop();/* 生成一个新的 Split 状态 s,指向 e 的 start 状态和 -转移*/s = state(g, Split, e.start, NULL);/* 生成一个新的 Frag,用 Split

16、的 start 状态作为 start 状态,终止状态为 e 和 s 的终止状态的连接,压入栈中 */push(frag(s, append(e.out, list1(break;case *+256: 星节点的构造e = pop();/* 生成一个新的 Split 状态 s,指向 e1 的 start 状态和 -转移*/s = state(g, Split, e.start, NULL);/* e 的下一个状态回指 s */patch(e.out, s);/* 生成一个新的 Frag,用 Split 的 start 状态作为 start 状态,终止状态为 s 的终止状态,压入栈中 */push

17、(frag(s, list1(break;case +256: 加号节点的构造e = pop();/* 生成一个新的 Split 状态 s,开始状态为 e1 的 start 状态和终止状态为 -转移*/s = state(g, Split, e.start, NULL);patch(e.out, s);/* 生成一个新的 Frag,用 e 的 start 状态作为 start 状态,终止状态为 s 的终止状态,压入栈中 */push(frag(e.start, list1(break;下面是正则表达式(AT|GA)(AG|AAA)*) 的 NFA。图中阴影的状态是 Split 状态。0 12

18、34ATGA1 15 67 8AGAA9A1 01 24 DFA 构造DFA 算法的核心思想是,当使用 NFA 遍历文本时,会经过很多转移,因此会激活一个状态集合。然而,DFA 在一个时刻只有一个确定的活动状态,因此可以在 NFA 的状态集合上定义相对应的 DFA。该思想的关键在于,确定性自动机的当前唯一状态就是 NFA的当前活动状态集合。在 NFA 中,E(s)表示状态 s 对应的 闭包,它是在 NFA 中状态 s 能够通过 -转移到达的所有状态的集合。假设 NFA 为( Q, ,I ,F,) ,那么 DFA 定义为:其中, ,并且 。上述定义中的 (S, )表示:对于 S 中所有可能的活动

19、状态 s,可以通过标记为字符 的转移找到所有可能的状态 s,然后再从跟随所有可能的 -转移寻找其他状态。算法中 DFA 状态使用数据结构typedef struct _dStateList l; 指向一个数组,数组中是这个 DFA 状态对应的 NFA 状态的集合struct _dState *next256;转移表struct _dState *left;struct _dState *right;int id; 状态 idint flags; DState;Dstate 是用二叉树的形式组织起来的,以 l 作为关键字。算法的思想如下:build_dstate(DState *d)For Do

20、If (d, , Null) d = nextstate(d, ) ( d, , d)build_dstate(d)End IfEndfornextstate(DState *d, )For s d-l DoIf s-c = addstate(l, s-out);End IfEndford= dstate(l) 根据 l 生成一个 DstateReturn d将 s 状态上能够通过 -转移到达的状态放入 l 中,实际上就是 s 的 闭包addstate(List *l, State *s) If(s-c = Split) s 状态具有 -转移addstate(l, s-out);addstat

21、e(l, s-out1);Return l;EndIfl l sReturn l;下面是正则表达式(AT|GA)(AG|AAA)*) 的 DFA,加入了初始自环,即.*(AT|GA)(AG|AAA)*).012 459367 81 0AGGTAAGGTATAGAGAAGAGGTTTATAG上图中,黑色的箭头表示输入 A 后状态的转移,黄色的箭头表示输入 G,紫色的箭头表示输入 T 后状态的转移,红色的箭头表示其余的输入表示的状态转移,有两个圈的状态表示终止状态。使用 DFA 进行搜索十分简单,从状态 0 开始,依次读入字符,转移到下一个状态,如果这个状态是终止状态,则发现一个匹配,否则继续读入

22、字符,直到读完为止。5 Glushkov 自动机Glushkov 自动机通过只对字符计数,可以标记出字符表 中每个字符在正则表达式RE 中的位置。例如,(AT|GA)(AG|AAA)*)标记为(A 1T2|G3A4)(A5G6|A7A8A9)*)。我们使用表示对正则表达式 RE 进行标记的标记表达式,使用 L( )表示它对应的语言,其中每个字符都包含它的位置的索引。于是,上面的例子正则表达式的语言为 A1T2, G3A4, A1T2A5G6, 。用 Pos( ) = 1m表示中位置的集合,用 表示标记后的字母表。首先,在标记表达式 上构造自动机,这个自动机识别语言 L( )。然后,通过消除所有

23、字符的位置索引,可以从中抽取 Glushkov 自动机,从而识别语言 L(RE)。字符位置的集合,加上一个初始状态 0,可以当做自动机状态集合的索引。创建 m+1个状态,标记为 0 到 m。状态 j 表示已经从文本中读取了一个字符串,该字符串在 NFA 中的位置 j 结束。当再读入一个新的字符 时,需要知道从位置 j 通过 可以到达的位置。这个位置可以通过 Glushkov 自动机计算出来。为了说明 Glushkov 算法,下面首先引入四个新的定义。其中, y 表示 中 y 处的被索引字符。集合 First( )表示 L( )的初始状态集合,也就是说,在这些位置可以开始读入字符。上面的例子中,

24、First(A 1T2|G3A4)(A5G6|A7A8A9)*)=1,3。集合 Last( )表示 的终止状态集合,也就是说,在这些位置可以识别出 L(RE)中的字符串。上面的例子中,Last(A 1T2|G3A4)(A5G6|A7A8A9)*)=2, 4, 6, 9。集合 Follow( )表示在 Pos( )中从 x 可以到达的所有位置。上面的例子中,从位置6 可以到达的位置集合为 Follow(A1T2|G3A4)(A5G6|A7A8A9)*), 6)=7, 5。函数 EmptyRE 可以递归的定义如下:当 属于 L(RE)时,它的值为 ;否则它的值为 。确定的 Glushkov 自动机

25、 可以识别语言 L( ),它可以通过下面的方法进行构造:其中,(i)S = 0, 1, , m是状态集合,它是位置 Pos( )的集合,初始状态为 I = 0。(ii)F = Last( )(EmptyRE 0)是终止状态集合。通常,如果状态(位置)i 属于Pos( ),则 i 是一个终止状态。当空字 属于 L( )时,初始状态 0 也是终止状态。这时,Empty RE = ,则 EmptyRE 0= 0;否则 EmptyRE 0= (iii ) 是自动机的转移函数,定义为:(5.1)通常情况下,如果状态 y 在状态 x 之后,那么从 x 到 y 有一个转移 y。从初始状态开始的转移定义为:(

26、5.2)图给出了标记正则表达式对应的 Glushkov 自动机。为了获得原始的 RE 的 Glushkov,只要简单地去掉标记自动机的位置索引即可。在这个过程中,自动机通常会变成非确定的。新的自动机对应语言 L(RE)。例子(A 1T2|G3A4)(A5G6|A7A8A9)*)对应的 Glushkov 自动机如所示。Glushkov 算法基于正则表达式 RE 对应的树表示 TRE。树中的每个节点 v 都代表 RE的子表达式 REv。算法中使用了下列与 v 相关的变量:First(v):表示集合中所有位置的列表。Last(v):表示集合中所有位置的列表。Emptyv:如果中包含空串 ,它为 Tr

27、ue,否则为 False。对于每个节点,这些变量都是后序计算的,也就是说,先计算出 v 的所有子节点的变量值,然后再计算 v 的变量值。如果 v 是”|”或者”.”,则把它的两个子节点分别记做 vl 和vr;如果 v 表示”*”,则把它唯一的子节点记做 v*。集合 Follow(x)是一个全局变量,对于每一个节点 v,Follow(x)的值要根据它在子表达式中位置的变化而不断更新。Glushkov_variables(vRE, lpos)If v = | (vl,vr) OR v = (vl,vr) Thenlpos Glushkov_variables (v l,lpos)lpos Glus

28、hkov_variables (v r,lpos)Else If v = * (v*) Then lpos Glushkov_variables (v *,lpos)End of IfIf v= () ThenFirst(v) ,Last(v ) , Emptyv Then If v = (), Thenlpos lpos + 1First(v) lpos , Last(v) lpos, Emptyv , Follow(lpos) Then If v = | (vl,vr) ThenFirst(v) First( vl) First(vr)Last (v) Last (vl) Last (vr

29、)Emptyv Empty vl Empty vrThen If v = (vl,vr) Then First(v) First( vl) (Empty vl First(vr)Last (v) ( Emptyvr Last (vl) Last (vr)Emptyv Empty vl EmptyvrFor x Last( vl) Do Follow(x) Follow (x) First( vr)Then If v = * (v*) ThenFirst(v) First( v*), Last(v) Last(v *), Emptyv For x Last(v*) Do Follow(x) Fo

30、llow (x) First(v *)End of IfReturn lposGlushkov(RE)vRE Parse (RE,1)m Glushkov_variables (vRE, 0)= For i 0m Do create state iFor x First(vRE) Do(0, x, x)For i 0m DoFor x Follow(i) Do(i, x, x)End of forFor x Last(vRE) (Empty vRE 0) Do mark x as terminal上面给出了 Glushkov_variables(vRE,lpos)的递归算法。对于正则表达式 R

31、E 的树表示中的每个节点 v,该算法提供计算变量 First(v),Last(v),Follow(x) 和 Emptyv 的方法。为了计算每个节点 vRE 的值,该算法使用后续遍历树中所有的节点。整个 Glushkov 算法包括:将 RE 转化成树 vRE,并且使用 Glushkov_variables(vRE,0)计算它对应的变量,然后从树 vRE 的根对应的变量开始构造 Glushkov 自动机。6 Glushkov 自动机的位并行算法存储 DFA 状态(也就是 NFA 的多个状态集合)的一种可行方法就是使用 O(m)位的位掩码。其中,如果 NFA 的第 i 个状态属于 DFA,那么位掩码

32、的第 i 位就为 1。NFA( )可以表示如下:, , (即对终止状态位置的位或运算) 。使用位掩码的 Glushkov_variables 算法如下所示:为了说明使用位掩码表示 Glushkov 算法,我们先介绍 Glushkov 自动机的几个性质。(i( Glushkov 自动机的 NFA 是 -free的证明:由公式,标记正则表达式的转移函数为 ,即当前为 x 状态,输入y,转移到状态 y。注意 y 是正则表达式第 y 个字符和 y 的组合,显然不为空。转换后的正则表达式只是去掉位置索引,因此转移函数中 y 为正则表达式第 y 个字符,也是不为空的。故自动机的 NFA 是 -free 的

33、。(ii( 所有指向状态 y 的箭头都标记着同样的字符证明:同样,根据转移函数 ,到达状态 y 的输入是 y,显然是同一个字符。(iii( 假设 B()为包含字符 的位置的集合,则有:(6.1)证明:设 y(x, )。这意味着 y 能够从 x 位置输入 到达,因此。相反,假设 ,那么表明可以从 x 位置转移到 y,并且输入 也能到达 y。根据性质 (ii) ,到达 y 的所有箭头都标记为 ,显然也包括离开 x 的,因此 y(x, ) 。(iv( 不会有任何箭头到达 Glushkov 自动机的 NFA 中的 0 状态证明:所有的箭头都是根据公式生成的,并且根据 Follow 函数的定义,0 状态

34、不在任何其他状态的 Follow 状态集合中。下面我们使用性质 (iii)来获得 DFA 的表示。计算 DFA 的转移表可以通过两个表来进行,一个是 B,给出了每个字符可达到状态的位掩码。第二个是 Follow 的确定性版本,一个从状态的集合到状态的集合的表 T(位掩码形式) ,满足:(6.2)这个表给出了从一个 D 中的激活状态,不管通过哪个字符,可到达的状态集合。由性质 (iii ) ,下式成立:(6.3)注意,存储 :2 m+12 m+1,需要( m+1)(2m+1)位,而存储 T:2 m+12 m+1 和B:2 m+1 需要( m+1)(2m+1+)位,节省了很多的空间。下面给出从 F

35、ollow 计算 T 的算法:我们现在给出基于位掩码以及上面构建的搜索算法。首先,用 First 和 Last 表示整个正则表达式的对应的变量。从技术的便利上看,可以设置 Follow(0)=First。其次,我们增加一个自循环在表达式的开始,以便可以搜索到从任意位置开始的正则表达式的语言。下面给出搜索算法:上面提到,存储 T 和 B 需要(最坏的情况) (m+1)(2m+1+)位。但是在经典的 DFA 算法中,只需要存储能够达到的状态,这个状态远小于 2m+1 所有可能的状态。这里也可以使用类似的算法,只计算可以到达的 T。下面的算法给出了递归构建 T 的过程,从D = 0m1 开始,假定

36、T 已经初始化为 0,B,Follow 和 m 已经计算出来了。7 位并行 Glushkov 算法的实现7.1 位掩码我们使用位掩码来表示集合。位掩码实际上是内存中一块连续的内存,内存地址低的是位掩码的低位。程序中提供了一系列对位掩码的操作函数和宏。如下所示:static BITMAP* makebitmap( unsigned numbytes,unsigned num );生成 num 个位掩码,每个占用 numbytes 个字节,返回指向位掩码的指针;void free_bitmap(BITMAP* map)释放位掩码的空间;SETBIT(c,map,val)宏,设置位掩码 map 的第

37、 c 位的值为 val(0 或 1);TESTBIT(c,map)宏,位掩码 map 的第 c 位是否为 1?;BITCOPY(size,D,S)宏,拷贝位掩码,DS, size 个字 节;TRAVEL_BITMAP_BEGIN(size,map,i)TRAVEL_BITMAP_END遍历位掩码 map 中所有为 1 的位;int compare_bitmap(int numbytes, BITMAP *A, BITMAP* B)比较位掩码 A 和 B 的大小,从最高字 节开始,逐个字 节比 较,每个字节看成无符号数;void bitmap_AND(int numbytes, BITMAP *

38、C, BITMAP* A, BITMAP* B)位掩码与运算,C=Aunsigned int value;unsigned int key;BITMAP* D;BITMAP* T;hash_key_node;typedef struct hash_nodestruct hash_key_node* head;unsigned int value; /这个 key 下面有几个节点hash_node;typedef struct hashtableunsigned int bucket_num;hash_node* bucket;hashtable;每个 hash_key_node 节点中都存储了

39、 D 和 T,保存 D 目的是在插入一个相同 key 时,比较是否 D 相同。由于在生成 T 表前不知道有效的 T 的个数,故 hash 表的大小是动态变化的。为了不会在一个 Key 下面挂多个节点,在当前 T 的表项个数大于 2/3 的hash 表的 bucket 数目时,就重新生成一个更大数目的表。7.3Glushkov 算法的实现程序中实现的 Glushkov 算法和上面的伪代码算法很相似,这里就不在详述了。8 一个具体的例子正则表达式为(AT|GA)(AG|AAA)*) ,字符串为 AAAGATAAGATAGAAAA。B 表如下:(只显示所用到的)BA(0x41):11 1011001

40、1 BG(0x47):00 01001001 BT(0x54):00 00000101 T 表如下:Hash0 :T00 00000000 =00 00000000 Hash6 :T00 00010011 =00 10101111 Hash14:T00 00001001 =00 00011011 Hash18:T00 10100011 =01 01001111 Hash19:T01 10100011 =11 01001111 Hash20:T10 10100011 =01 11101111 Hash31:T00 00000001 =00 00001011 Hash37:T00 10110011

41、 =01 11101111 T00 01001001 =00 10111011 Hash40:T00 00000011 =00 00001111 Hash41:T01 00000011 =10 00001111 Hash42:T10 00000011 =00 10101111 Hash43:T11 00000011 =10 10101111 Hash49:T00 00000101 =00 10101011 Final: 10 01010100从 D 等于00 00000001 开始:1. 读入 A T00 00000001 00 00001011 & BA:11 10110011 D:00 0

42、0000011 2. 读入 A T00 00000011 00 00001111 & BA:11 10110011 D:00 00000011 3. 读入 A T00 00000011 00 00001111 & BA:11 10110011 D:00 00000011 4. 读入 G T00 00000011 00 00001111 & BG:00 01001001 D:00 00001001 5. 读入 A T00 00001001 00 00011011 & BA:11 10110011 D:00 00010011 Final:10 01010100因为 D&F 0m+1,所以标记一个成

43、功匹配6. 读入 T T00 00010011 00 10101111 & BT:00 00000101 D:00 00000101 Final:10 01010100因为 D&F 0m+1,所以标记一个成功匹配7. 读入 A T00 00000101 00 10101011 & BA:11 10110011 D:00 10100011 8. 读入 A T00 10100011 01 01001111 & BA:11 10110011 D:01 00000011 9. 读入 G T01 00000011 10 00001111 & BG:00 01001001 D:00 00001001 10

44、. 读入 A T00 00001001 00 00011011 & BA:11 10110011 D:00 00010011 Final:10 01010100因为 D&F 0m+1,所以标记一个成功匹配11. 读入 T T00 00010011 00 10101111 & BT:00 00000101 D:00 00000101 Final:10 01010100因为 D&F 0m+1,所以标记一个成功匹配12. 读入 A T00 00000101 00 10101011 & BA:11 10110011 D:00 10100011 13. 读入 G T00 10100011 01 0100

45、1111 & BG:00 01001001 D:00 01001001 Final:10 01010100因为 D&F 0m+1,所以标记一个成功匹配14. 读入 A T00 01001001 00 10111011 & BA:11 10110011 D:00 10110011 Final:10 01010100因为 D&F 0m+1,所以标记一个成功匹配15. 读入 A T00 10110011 01 11101111 & BA:11 10110011 D:01 10100011 16. 读入 A T01 10100011 11 01001111 & BA:11 10110011 D:11 00000011 Final:10 01010100因为 D&F 0m+1,所以标记一个成功匹配17. 读入 A T11 00000011 10 10101111 & BA:11 10110011 D:10 10100011Final:10 01010100因为 D&F 0m+1,所以标记一个成功匹配

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 实用文档 > 统计图表

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报