1 module miao..string.quick_search; 2 3 /*! 4 Main features 5 simplification of the Boyer-Moore algorithm; 6 uses only the bad-character shift; 7 easy to implement; 8 preprocessing phase in O(m+sigma) time and O(sigma) space complexity; 9 searching phase in O(mn) time complexity; 10 very fast in practice for short patterns and large alphabets. 11 12 Description 13 The Quick Search algorithm uses only the bad-character shift table (see chapter Boyer-Moore algorithm). After an attempt where the window is positioned on the text factor y[j .. j+m-1], the length of the shift is at least equal to one. So, the character y[j+m] is necessarily involved in the next attempt, and thus can be used for the bad-character shift of the current attempt. 14 The bad-character shift of the present algorithm is slightly modified to take into account the last character of x as follows: for c in Sigma, qsBc[c]=min{i : 0 < i leq m and x[m-i]=c} if c occurs in x, m+1 otherwise (thanks to Darko Brljak). 15 The preprocessing phase is in O(m+sigma) time and O(sigma) space complexity. 16 During the searching phase the comparisons between pattern and text characters during each attempt can be done in any order. The searching phase has a quadratic worst case time complexity but it has a good practical behaviour. 17 */ 18 19 @trusted: 20 21 import miao.common.skip_table; 22 import miao.common.check; 23 import miao.common.util; 24 25 version(unittest) import std.stdio; 26 27 struct Quick_search_searcher(PatRange, CorpusRange = PatRange) 28 if (isValidParam!(PatRange, CorpusRange)) { 29 public: 30 this(in PatRange pattern) 31 { 32 pattern_ = pattern; 33 34 if (pattern_.length > 0) { 35 skip_ = build_qs_table(pattern_); 36 } 37 } 38 39 int search(in CorpusRange corpus) const 40 out(result) { 41 assert(result == -1 || (0 <= result && result < corpus.length)); 42 } 43 body { 44 if (corpus.length == 0 || pattern_.length == 0) return -1; 45 if (corpus.length < pattern_.length) return -1; 46 47 return search_(corpus); 48 } 49 50 private: 51 int search_(in CorpusRange corpus) const 52 { 53 const cmp_len = corpus.length - pattern_.length; 54 55 auto window_pos = 0; 56 while (window_pos < cmp_len) { 57 const window = corpus[window_pos .. window_pos + pattern_.length]; 58 59 if (window == pattern_) { 60 return window_pos; 61 } 62 63 //! sliding window 64 const bad_char = corpus[window_pos + pattern_.length]; 65 window_pos += skip_[bad_char]; 66 } 67 68 if (window_pos == cmp_len && corpus[window_pos .. $] == pattern_) { 69 return window_pos; 70 } 71 72 return -1; 73 } 74 75 private: 76 immutable Skip_table!(ValueType!PatRange) skip_; 77 const PatRange pattern_; 78 } 79 80 alias quick_search = GenerateFunction!Quick_search_searcher; 81 82 unittest { 83 import miao.string.test; 84 85 testAll!(Quick_search_searcher, quick_search)(); 86 }