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 }