1 module miao..string.knuth_morris_pratt; 2 3 /*! 4 performs the comparisons from left to right; 5 preprocessing phase in O(m) space and time complexity; 6 searching phase in O(n+m) time complexity (independent from the alphabet size); 7 delay bounded by logPhi(m) where Phi is the golden ratio ( golden ratio ). 8 9 */ 10 11 @trusted: 12 13 import miao.common.check; 14 import miao.common.util; 15 16 struct Knuth_morris_pratt_searcher(PatRange, CorpusRange = PatRange) 17 if (isValidParam!(PatRange, CorpusRange)) { 18 public: 19 this(in PatRange pattern) 20 { 21 pattern_ = pattern; 22 23 if (pattern_.length != 0) { 24 skip_ = build_skip_table_(); 25 } 26 } 27 28 int search(in CorpusRange corpus) const 29 out(result) { 30 assert(result == -1 || (0 <= result && result < corpus.length)); 31 } 32 body { 33 if (corpus.length == 0 || pattern_.length == 0) return -1; 34 if (corpus.length < pattern_.length) return -1; 35 36 return search_(corpus); 37 } 38 39 private: 40 pure int[] build_skip_table_() const nothrow 41 { 42 auto skip = new int[pattern_.length]; 43 44 skip[0] = -1; 45 46 int cursor = 2; 47 while (cursor < pattern_.length) { 48 int prev = cursor - 1; 49 int prefix_cursor = skip[prev]; 50 51 while (prefix_cursor >= 0 && pattern_[prev] != pattern_[prefix_cursor]) { 52 prefix_cursor = skip[prefix_cursor]; 53 } 54 55 prefix_cursor++; 56 57 skip[cursor] = prefix_cursor; 58 59 cursor++; 60 } 61 62 return skip; 63 } 64 65 int search_(in CorpusRange corpus) const 66 { 67 const cmp_len = corpus.length - pattern_.length; 68 const last_pos = pattern_.length - 1; 69 70 int window_pos = 0; 71 int cursor = 0; 72 73 while (window_pos <= cmp_len) { 74 assert(0 <= cursor && cursor < pattern_.length); 75 76 const window = corpus[window_pos .. window_pos + pattern_.length]; 77 78 // find the first mismatch 79 for (; window[cursor] == pattern_[cursor]; ++cursor) { 80 if (cursor == last_pos) return window_pos; 81 } 82 83 // move window and cursor 84 const prefix_cursor = skip_[cursor]; 85 window_pos += cursor - prefix_cursor; 86 cursor = prefix_cursor >= 0? prefix_cursor: 0; 87 88 assert(window_pos >= 0); 89 assert(0 <= cursor && cursor < pattern_.length); 90 } 91 92 return -1; 93 } 94 95 private: 96 const PatRange pattern_; 97 immutable int[] skip_; 98 } 99 100 alias knuth_morris_pratt_search = GenerateFunction!Knuth_morris_pratt_searcher; 101 102 unittest { 103 import miao.string.test; 104 105 testAll!(Knuth_morris_pratt_searcher, knuth_morris_pratt_search)(); 106 }