QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3481|回复: 0
打印 上一主题 下一主题

[其他资源] 透彻理解Shift-And/Shift-Or 算法 字符串匹配/子串查找

[复制链接]
字体大小: 正常 放大

14

主题

10

听众

43

积分

升级  40%

  • TA的每日心情
    慵懒
    2015-5-5 09:46
  • 签到天数: 10 天

    [LV.3]偶尔看看II

    自我介绍
    跳转到指定楼层
    1#
    发表于 2015-4-13 10:06 |只看该作者 |正序浏览
    |招呼Ta 关注Ta
    Shift-And/Shift-Or 算法和KMP 算法一样,也是线性时间复杂度的字符串匹配算法,运行时间上甚至要比KMP 算法快得多。而理解上比KMP 算法更容易一些。Shift-And/Shift-Or 算法设计的非常巧妙,初次接触时同样“吓了一跳”。) ?+ K! H4 }* a& l3 r
    Shift-And 与 Shift-Or 算法的原理完全一样,区别仅在于Shift-Or 对Shift-And 做了一点儿改进。我们先说Shift-And 算法。
    1 [% F( J- ~  J: g  y  |7 K2 W1 Y8 `' X5 u+ j' M8 a
    S 表示原字符串,T 表示目标串(模式串),我们要在S 中搜索T。
    + j, A" `! e1 i, s令 S[0..m-1] = abcabcabdabba, T[0..n-1] = abcabd
    ' S  I5 h$ g& M  ?2 }$ N' S5 D' B
    1,Shift-And 算法思想8 V1 V0 b) m9 q( K' k& _% e
    Shift-And 算法的核心思想是利用掩码D 来记录模式串的前缀匹配情况。(瞧,shift 算法的核心也是前缀匹配)。Shift 算法大量应用了位运算。8 {4 \/ }" Z+ @& v( R: |# t
    D 是一个m 位的无符号整数:D[n-1, n-2, ..,1,0] (注意D 并不是一个数组,仅仅是一个整数,D[n-1] 表示其最高位bit)。( }; q2 G! x+ |; B6 h" s
    数组索引i 控制S 串的扫描,当扫描的字符S 时,D 的第j 位D[j] = 1 当且仅当T[0..j] 是S[0..i] 的一个后缀。
    + M2 m- q, |. r8 J+ V& F" z  w
    5 z8 [1 m4 q6 Q. y4 g* M要使用Shift 算法,需要一个辅助表B。B 是一个字典,key 是问题域字符集中的每个字符,value 是一个n 位无符号整数,记录该字符在模式串T 的哪些位置出现。
    ; ~/ z5 A+ N' j7 ~7 g3 v) O例如,字符c 在T[2] 处出现,那么B['c'] = 000100 (对于字符串,低位在左;对于B['c'],低位在右);同理,a 在T[0],T[3] 处出现,B['a'] = 001001.
    ' y7 d; ?5 S0 X7 q" s' S7 v: H& t* v" u7 K
    假设当前处理到S,需要对D 进行更新。由于D[j] (0<j<n) 标识T[0..j] 是否是S[0..i] 的后缀,所以D[j]=1 当且仅当更新前的D[j-1]=1 并且S==T[j];D[0] 是边界情况,D[0]=1 当且仅当S==T[0]。
    5 s  `8 {5 a1 i# z所以,D = (D << 1 | 1) & B[S[i] ;
    ) Z3 _0 E  {! v显然,当D[n-1]=1 时,表示T[0..n-1] 是S[0..i] 的后缀,此时找到一个T的完全匹配。
    % E; ~. c( S0 s" q; M, T/ h$ `) n) ]5 x2 g
    2,Shift-And 算法实现
    ) J) Q, H8 h" y  m0 k. r9 Z* m, GShift-And 匹配过程代码:
    % }3 T6 ^: r5 M
    9 ?  e, h& p2 Q  \

    ; E* V+ q+ s5 }* f" ^6 @2 ?由于位运算在计算机中可以并行进行,每次循环的执行是常数时间的,所以上面代码段的复杂度是 O(m)。% v/ N/ B+ d/ E+ n5 @' m

    & T: }2 g/ n; I+ E5 R$ S- I, H) p) R3,辅助表 B
    % E0 l& i3 `9 T9 S- k0 x# @: a6 \6 |- X上面没有提到如何得到辅助表B。很简单,只要获得模式串T 中每个字符出现的位置。0 \/ p- o0 l  b) w

    & H4 t/ z. a* g. x* |; k2 ]+ g1 ?! T! G2 `. i- F: F! C0 S
    显然,上述代码段的复杂度是 O(n)。Shift-And 算法的时间复杂度是O(m+n)。
    ; z# p1 {' j3 P* y& s* I" b实际上,shift 算法通常比KMP 算法的匹配速度要快,因为计算机位并行运算是非常高效的。# V, f% B% n+ p. ]4 U' ?

    # E! Y4 F$ b$ G. q: F5 B注意:数组B 的大小是由字符集决定的,如果字符来自ASCII 码,字符的数值范围是0~127,数组大小是128 即可;否则,可能需要更大的数组B,或者自己构建字符到整数索引之间的散列关系。$ R: G! ?4 `6 J1 M- @9 b

    : D& T, Z7 h& W: E% t! B4,Shift-Or 算法' |* a) E6 B3 N/ L
    在Shift-And 中,对掩码D 的更新:D = (D << 1 | 1) & B[S[i] ;
    + L  K4 C! `# b% h. b每次更新D 都需要额外进行D 移位后与"1" 的"或"运算。这是由于我们要保证当字符S 在T[0] 处出现时,D[0] 一定要等于1,而D 向左移位后最低位是0。( D% z, v/ |4 I# I. Y$ q

    % p- ^5 [+ H4 x- @6 K$ n如果将Shift-And 中核心的“与” 运算改为“或” 运算,可以节省这一个附加的“或1” 运算。这正是Shift-Or 所改进的地方。
    . H$ k  h2 R8 ~Shift-Or 与Shift-And 的唯一区别在于,在Shift-Or 中,“有效位” 是通过0(而不是1)来标识。  I- x1 n' n" q4 E+ s4 S
    于是求解辅助表B 和更新掩码D 都会与Shift-And 有一些区别,详见代码。
    9 U4 `8 v% C% n2 O
    8 H( t1 e+ q3 |. z  d" FShift-And 完整代码:C++ 实现  Python 实现+ \0 I6 k1 Z; d7 h1 i  h
    Shift-Or 完整代码:C++ 实现  Python 实现
    & o0 \4 @, O, r# o# U5 E
    4 O& }7 Y1 @2 g- ^) `1 q
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-8-23 12:20 , Processed in 0.376269 second(s), 55 queries .

    回顶部