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 }