diff --git a/README.md b/README.md index ce93985d..c12ca339 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ Various Math algorithms: Various String algorithms: - [Levenshtein Distance](https://github.com/manrajgrover/algorithms-js/blob/master/src/algorithms/string/levenshtein_distance.js) +- [Z-Algorithm](https://github.com/manrajgrover/algorithms-js/blob/master/src/algorithms/string/zalgorithm.js) + #### Geometry Various Geometry algorithms: diff --git a/src/algorithms/string/index.js b/src/algorithms/string/index.js index ad535890..90c81513 100644 --- a/src/algorithms/string/index.js +++ b/src/algorithms/string/index.js @@ -1,5 +1,7 @@ const levenshteindistance = require('./levenshtein_distance'); +const zalgorithm = require('./zalgorithm'); module.exports = { - levenshteindistance + levenshteindistance, + zalgorithm }; diff --git a/src/algorithms/string/zalgorithm.js b/src/algorithms/string/zalgorithm.js new file mode 100644 index 00000000..525cc141 --- /dev/null +++ b/src/algorithms/string/zalgorithm.js @@ -0,0 +1,51 @@ +/** + * @param {string} text + * @param {string} pattern + * @return {Array.|null} returns a list of index which is the begginning of the pattern +*/ +const zAlgorithm = (text, pattern) => { + if (!text || !pattern) { return null; } + + const str = pattern + '$' + text; + const z = new Array(str.length).fill(0); + + let l = 0; + let r = 1; + let k = 0; + + for (let i = 0; i < z.length; i++) { + if (i > r) { + l = r = i; + while (r < z.length && str[r-l] === str[r]) { + r++; + } + z[i] = r-l; + r--; + } + else { + k = i-l; + if (z[k] < r-i+1) { + z[i] = z[k]; + } + else { + l = i; + while (r < z.length && str[r-l] === str[r]) { + r++; + } + z[i] = r-l; + r--; + } + } + } + + let ret = []; + for (let i = 0; i < z.length; i++) { + if (z[i] >= pattern.length) { + ret.push(i - pattern.length - 1); + } + } + + return ret; +}; + +module.exports = zAlgorithm; diff --git a/test/algorithms/string/testZAlgorithm.js b/test/algorithms/string/testZAlgorithm.js new file mode 100644 index 00000000..d55c447e --- /dev/null +++ b/test/algorithms/string/testZAlgorithm.js @@ -0,0 +1,53 @@ +/* eslint-env mocha */ +const zalgorithm = require('../../../src').algorithms.string.zalgorithm; +const assert = require('assert'); + +describe('Z Algorithm', () => { + it('should return [4]', () => { + const text = 'xaayaab'; + const pattern = 'aab'; + + const result = zalgorithm(text, pattern); + assert.deepEqual(result, [4]); + }); + + it('should return [0,5,9]', () => { + const text = 'aabxcaabxaabxay'; + const pattern = 'aabx'; + + const result = zalgorithm(text, pattern); + assert.deepEqual(result, [0,5,9]); + }); + + it('should return [6]', () => { + const text = 'abxabcabcaby'; + const pattern = 'abcaby'; + + const result = zalgorithm(text, pattern); + assert.deepEqual(result, [6]); + }); + + it('should return null when text or pattern is empty', () => { + const text = ''; + const pattern = ''; + + const result = zalgorithm(text, pattern); + assert.deepEqual(result, null); + }); + + it('should return [0,54]', () => { + const text = 'Mississippi is a beautiful state with lots of rivers. Mississippi River is the longest river in the United States.'; + const pattern = 'Mississippi'; + + const result = zalgorithm(text, pattern); + assert.deepEqual(result, [0,54]); + }); + + it('should return [6,194]', () => { + const text = 'Mount Everest, also known as Sagarmatha in Nepal, is the world\'s tallest mountain. It is a part of the Himalayas and has long been a challenge for climbers. The first successful ascent of Mount Everest was in 1953 by Sir Edmund Hillary and Tenzing Norgay.'; + const pattern = 'Everest'; + + const result = zalgorithm(text, pattern); + assert.deepEqual(result, [6,194]); + }); +});