mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-09-21 00:44:03 +08:00
Z algorithm implementation (#77)
* Implemented Z algorithm * Fixed bugs in implementation and added tests * Added README explaining z algorithm
This commit is contained in:
parent
d74234d597
commit
9e210ae560
27
src/algorithms/string/z-algorithm/README.md
Normal file
27
src/algorithms/string/z-algorithm/README.md
Normal 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 `1 ≤ L ≤ i ≤ R`
|
||||||
|
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|)`
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
57
src/algorithms/string/z-algorithm/zAlgorithm.js
Normal file
57
src/algorithms/string/z-algorithm/zAlgorithm.js
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user