From 5bdcbb397d0440b3a67970927cd86d9a2d44db1d Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Thu, 21 Jun 2018 16:59:20 +0300 Subject: [PATCH] Code style fixes. --- README.md | 3 +- .../z-algorithm/__test__/zAlgorithm.test.js | 18 ++++---- .../string/z-algorithm/zAlgorithm.js | 41 ++++++++++++++----- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 740aae03..690c80ab 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ a set of rules that precisely define a sequence of operations. * **Strings** * `A` [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences * `B` [Hamming Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/hamming-distance) - number of positions at which the symbols are different - * `A` [Knuth–Morris–Pratt Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/knuth-morris-pratt) (KMP Algorithm) - substring search + * `A` [Knuth–Morris–Pratt Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/knuth-morris-pratt) (KMP Algorithm) - substring search (pattern matching) + * `A` [Z Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/z-algorithm) - substring search (pattern matching) * `A` [Rabin Karp Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/rabin-karp) - substring search * `A` [Longest Common Substring](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-substring) * **Searches** diff --git a/src/algorithms/string/z-algorithm/__test__/zAlgorithm.test.js b/src/algorithms/string/z-algorithm/__test__/zAlgorithm.test.js index d58ff75b..9ef1e3f4 100644 --- a/src/algorithms/string/z-algorithm/__test__/zAlgorithm.test.js +++ b/src/algorithms/string/z-algorithm/__test__/zAlgorithm.test.js @@ -1,12 +1,16 @@ import zAlgorithm from '../zAlgorithm'; describe('zAlgorithm', () => { - it('should find word position in given text', () => { - expect(zAlgorithm('abcbcglx', 'abca')).toBe(-1); - expect(zAlgorithm('abcbcglx', 'bcgl')).toBe(3); - expect(zAlgorithm('abcxabcdabxabcdabcdabcy', 'abcdabcy')).toBe(15); - expect(zAlgorithm('abcxabcdabxabcdabcdabcy', 'abcdabca')).toBe(-1); - expect(zAlgorithm('abcxabcdabxaabcdabcabcdabcdabcy', 'abcdabca')).toBe(12); - expect(zAlgorithm('abcxabcdabxaabaabaaaabcdabcdabcy', 'aabaabaaa')).toBe(11); + it('should find word positions in given text', () => { + expect(zAlgorithm('abcbcglx', 'abca')).toEqual([]); + expect(zAlgorithm('abca', 'abca')).toEqual([0]); + expect(zAlgorithm('abca', 'abcadfd')).toEqual([]); + expect(zAlgorithm('abcbcglabcx', 'abc')).toEqual([0, 7]); + expect(zAlgorithm('abcbcglx', 'bcgl')).toEqual([3]); + expect(zAlgorithm('abcbcglx', 'cglx')).toEqual([4]); + expect(zAlgorithm('abcxabcdabxabcdabcdabcy', 'abcdabcy')).toEqual([15]); + expect(zAlgorithm('abcxabcdabxabcdabcdabcy', 'abcdabca')).toEqual([]); + expect(zAlgorithm('abcxabcdabxaabcdabcabcdabcdabcy', 'abcdabca')).toEqual([12]); + expect(zAlgorithm('abcxabcdabxaabaabaaaabcdabcdabcy', 'aabaabaaa')).toEqual([11]); }); }); diff --git a/src/algorithms/string/z-algorithm/zAlgorithm.js b/src/algorithms/string/z-algorithm/zAlgorithm.js index f93318ce..743e085d 100644 --- a/src/algorithms/string/z-algorithm/zAlgorithm.js +++ b/src/algorithms/string/z-algorithm/zAlgorithm.js @@ -1,12 +1,13 @@ +// The string separator that is being used for "word" and "text" concatenation. +const SEPARATOR = '$'; + /** - * @param {string} word - * @param {string} text + * @param {string} zString * @return {number[]} */ - -function buildZArray(word, text) { - const zString = `${word}$${text}`; +function buildZArray(zString) { const zArray = new Array(zString.length); + let left = 0; let right = 0; let k = 0; @@ -44,14 +45,32 @@ function buildZArray(word, text) { /** * @param {string} text * @param {string} word - * @return {number} + * @return {number[]} */ export default function zAlgorithm(text, word) { - const zArray = buildZArray(word, text); - for (let i = 1; i < zArray.length; i += 1) { - if (zArray[i] === word.length) { - return (i - word.length - 1); + // The list of word's positions in text. Word may be found in the same text + // in several different positions. Thus it is an array. + const wordPositions = []; + + // Concatenate word and string. Word will be a prefix to a string. + const zString = `${word}${SEPARATOR}${text}`; + + // Generate Z-array for concatenated string. + const zArray = buildZArray(zString); + + // Based on Z-array properties each cell will tell us the length of the match between + // the string prefix and current sub-text. Thus we're may find all positions in zArray + // with the number that equals to the length of the word (zString prefix) and based on + // that positions we'll be able to calculate word positions in text. + for (let charIndex = 1; charIndex < zArray.length; charIndex += 1) { + if (zArray[charIndex] === word.length) { + // Since we did concatenation to form zString we need to subtract prefix + // and separator lengths. + const wordPosition = charIndex - word.length - SEPARATOR.length; + wordPositions.push(wordPosition); } } - return -1; + + // Return the list of word positions. + return wordPositions; }