github.com/scottcagno/storage@v1.8.0/pkg/search/knuth-morris-pratt.go (about)

     1  package search
     2  
     3  // KnuthMorrisPratt algorithm is oftentimes only the best performing when it's used on shorter texts or
     4  // if you are pre-computing the search tables beforehand. Otherwise, Boyer-Moore (and even Rabin-Karp) will
     5  // beat it almost out most of the time.
     6  type KnuthMorrisPratt struct{}
     7  
     8  func NewKnuthMorrisPratt() *KnuthMorrisPratt {
     9  	return new(KnuthMorrisPratt)
    10  }
    11  
    12  func (kmp *KnuthMorrisPratt) String() string {
    13  	return "KNUTH-MORRIS-PRATT"
    14  }
    15  
    16  func (kmp *KnuthMorrisPratt) FindIndex(text, pattern []byte) int {
    17  	if text == nil || pattern == nil {
    18  		return -1
    19  	}
    20  	return knuthMorrisPrattFinder(text, pattern)
    21  }
    22  
    23  func (kmp *KnuthMorrisPratt) FindIndexString(text, pattern string) int {
    24  	return knuthMorrisPrattFinderString(text, pattern)
    25  }
    26  
    27  func knuthMorrisPrattFinder(text, pattern []byte) int {
    28  	nn := kmpFinder(text, pattern)
    29  	if len(nn) > 0 {
    30  		return nn[0]
    31  	}
    32  	return -1
    33  }
    34  
    35  func knuthMorrisPrattFinderString(text, pattern string) int {
    36  	nn := kmpFinderString(text, pattern)
    37  	if len(nn) > 0 {
    38  		return nn[0]
    39  	}
    40  	return -1
    41  }
    42  
    43  var patternSize = 86
    44  
    45  func kmpFinder(s, sub []byte) []int {
    46  	next := preKMP(sub)
    47  	i, j := 0, 0
    48  
    49  	m, n := len(sub), len(s)
    50  
    51  	x, y := sub, s
    52  	var ret []int
    53  
    54  	//got zero target or want, just return empty result
    55  	if m == 0 || n == 0 {
    56  		return ret
    57  	}
    58  
    59  	//want string bigger than target string
    60  	if n < m {
    61  		return ret
    62  	}
    63  
    64  	for j < n {
    65  		for i > -1 && x[i] != y[j] {
    66  			i = next[i]
    67  		}
    68  		i++
    69  		j++
    70  
    71  		//fmt.Println(i, j)
    72  		if i >= m {
    73  			ret = append(ret, j-i)
    74  			//fmt.Println("find:", j, i)
    75  			i = next[i]
    76  		}
    77  	}
    78  
    79  	return ret
    80  }
    81  
    82  func preMP(x []byte) []int {
    83  	var i, j int
    84  	length := len(x) - 1
    85  	//var mpNext [patternSize]int
    86  	mpNext := make([]int, len(x)+1)
    87  	i = 0
    88  	j = -1
    89  	mpNext[0] = -1
    90  
    91  	for i < length {
    92  		for j > -1 && x[i] != x[j] {
    93  			j = mpNext[j]
    94  		}
    95  		i++
    96  		j++
    97  		mpNext[i] = j
    98  	}
    99  	return mpNext
   100  }
   101  
   102  func preKMP(x []byte) []int {
   103  	var i, j int
   104  	length := len(x) - 1
   105  	//var kmpNext [patternSize]int
   106  	kmpNext := make([]int, len(x)+1)
   107  	i = 0
   108  	j = -1
   109  	kmpNext[0] = -1
   110  
   111  	for i < length {
   112  		for j > -1 && x[i] != x[j] {
   113  			j = kmpNext[j]
   114  		}
   115  
   116  		i++
   117  		j++
   118  
   119  		if x[i] == x[j] {
   120  			kmpNext[i] = kmpNext[j]
   121  		} else {
   122  			kmpNext[i] = j
   123  		}
   124  	}
   125  	return kmpNext
   126  }
   127  
   128  func kmpFinderString(s, sub string) []int {
   129  	next := preKMPString(sub)
   130  	i, j := 0, 0
   131  
   132  	m, n := len(sub), len(s)
   133  
   134  	x, y := []byte(sub), []byte(s)
   135  	var ret []int
   136  
   137  	//got zero target or want, just return empty result
   138  	if m == 0 || n == 0 {
   139  		return ret
   140  	}
   141  
   142  	//want string bigger than target string
   143  	if n < m {
   144  		return ret
   145  	}
   146  
   147  	for j < n {
   148  		for i > -1 && x[i] != y[j] {
   149  			i = next[i]
   150  		}
   151  		i++
   152  		j++
   153  
   154  		//fmt.Println(i, j)
   155  		if i >= m {
   156  			ret = append(ret, j-i)
   157  			//fmt.Println("find:", j, i)
   158  			i = next[i]
   159  		}
   160  	}
   161  
   162  	return ret
   163  }
   164  
   165  func preMPString(x string) []int {
   166  	var i, j int
   167  	length := len(x) - 1
   168  	//var mpNext [patternSize]int
   169  	mpNext := make([]int, len(x)+1)
   170  	i = 0
   171  	j = -1
   172  	mpNext[0] = -1
   173  
   174  	for i < length {
   175  		for j > -1 && x[i] != x[j] {
   176  			j = mpNext[j]
   177  		}
   178  		i++
   179  		j++
   180  		mpNext[i] = j
   181  	}
   182  	return mpNext
   183  }
   184  
   185  func preKMPString(x string) []int {
   186  	var i, j int
   187  	length := len(x) - 1
   188  	//var kmpNext [patternSize]int
   189  	kmpNext := make([]int, len(x)+1)
   190  	i = 0
   191  	j = -1
   192  	kmpNext[0] = -1
   193  
   194  	for i < length {
   195  		for j > -1 && x[i] != x[j] {
   196  			j = kmpNext[j]
   197  		}
   198  
   199  		i++
   200  		j++
   201  
   202  		if x[i] == x[j] {
   203  			kmpNext[i] = kmpNext[j]
   204  		} else {
   205  			kmpNext[i] = j
   206  		}
   207  	}
   208  	return kmpNext
   209  }