Z algorithm implementation (#77)

* Implemented Z algorithm

* Fixed bugs in implementation and added tests

* Added README explaining z algorithm
This commit is contained in:
hariv 2018-06-21 06:42:13 -07:00 committed by Oleksii Trekhleb
parent d74234d597
commit 9e210ae560
3 changed files with 96 additions and 0 deletions

View File

@ -0,0 +1,27 @@
# Z-algorithm
The Z-algorithm finds occurrences of a "word" `W`
within a main "text string" `T` in linear time.
Given a string `S` of length `n`, the algorithm produces
an array, `Z` where `Z[i]` represents the ongest substring
starting from `S[i]` which is also a prefix of `S`. Finding
`Z` for the string obtained by concatenating the word, `W`
with a nonce character, say `$` followed by the text, `T`,
helps with pattern matching, for if there is some index `i`
such that `Z[i]` equals the pattern length, then the pattern
must be present at that point.
While the `Z` array can be computed with two nested loops, the
following strategy shows how to obtain it in linear time, based
on the idea that as we iterate over the letters in the string
(index `i` from `1` to `n-1`), we maintain an interval `[L,R]`
which is the interval with maximum `R` such that `1LiR`
and `S[L...R]` is a prefix that is also a substring (if no such
interval exists, just let `L=R=-1`). For `i=1`, we can
simply compute `L` and `R` by comparing `S[0...]` to `S[1...]`.
## Complexity
- **Time:** `O(|W| + |T|)`
- **Space:** `O(|W|)`

View File

@ -0,0 +1,12 @@
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);
});
});

View File

@ -0,0 +1,57 @@
/**
* @param {string} word
* @param {string} text
* @return {number[]}
*/
function buildZArray(word, text) {
const zString = `${word}$${text}`;
const zArray = new Array(zString.length);
let left = 0;
let right = 0;
let k = 0;
for (let i = 1; i < zString.length; i += 1) {
if (i > right) {
left = i;
right = i;
while (right < zString.length && zString[right - left] === zString[right]) {
right += 1;
}
zArray[i] = right - left;
right -= 1;
} else {
k = i - left;
if (zArray[k] < (right - i) + 1) {
zArray[i] = zArray[k];
} else {
left = i;
while (right < zString.length && zString[right - left] === zString[right]) {
right += 1;
}
zArray[i] = right - left;
right -= 1;
}
}
}
return zArray;
}
/**
* @param {string} text
* @param {string} word
* @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);
}
}
return -1;
}