From e4faf7a85fc759a369b1d7a49df51f7a96ce2cc0 Mon Sep 17 00:00:00 2001 From: Sujan Chhetri <44917558+sujanchhetri@users.noreply.github.com> Date: Sat, 3 Oct 2020 23:34:13 +0530 Subject: [PATCH] Added StringSearch (#385) * Added StringSearch * fixed the standard issue --- Search/StringSearch.js | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 Search/StringSearch.js diff --git a/Search/StringSearch.js b/Search/StringSearch.js new file mode 100644 index 000000000..3cd9c1e3e --- /dev/null +++ b/Search/StringSearch.js @@ -0,0 +1,83 @@ +/* + * String Search + */ + +function makeTable (str) { + // create a table of size equal to the length of `str` + // table[i] will store the prefix of the longest prefix of the substring str[0..i] + const table = new Array(str.length) + let maxPrefix = 0 + // the longest prefix of the substring str[0] has length + table[0] = 0 + + // for the substrings the following substrings, we have two cases + for (let i = 1; i < str.length; i++) { + // case 1. the current character doesn't match the last character of the longest prefix + while (maxPrefix > 0 && str.charAt(i) !== str.charAt(maxPrefix)) { + // if that is the case, we have to backtrack, and try find a character that will be equal to the current character + // if we reach 0, then we couldn't find a chracter + maxPrefix = table[maxPrefix - 1] + } + // case 2. The last character of the longest prefix matches the current character in `str` + if (str.charAt(maxPrefix) === str.charAt(i)) { + // if that is the case, we know that the longest prefix at position i has one more character. + // for example consider `.` be any character not contained in the set [a.c] + // str = abc....abc + // consider `i` to be the last character `c` in `str` + // maxPrefix = will be 2 (the first `c` in `str`) + // maxPrefix now will be 3 + maxPrefix++ + // so the max prefix for table[9] is 3 + } + table[i] = maxPrefix + } + return table +} + +// Find all the words that matches in a given string `str` +function stringSearch (str, word) { + // find the prefix table in O(n) + const prefixes = makeTable(word) + const matches = [] + + // `j` is the index in `P` + let j = 0 + // `i` is the index in `S` + let i = 0 + while (i < str.length) { + // Case 1. S[i] == P[j] so we move to the next index in `S` and `P` + if (str.charAt(i) === word.charAt(j)) { + i++ + j++ + } + // Case 2. `j` is equal to the length of `P` + // that means that we reached the end of `P` and thus we found a match + // Next we have to update `j` because we want to save some time + // instead of updating to j = 0 , we can jump to the last character of the longest prefix well known so far. + // j-1 means the last character of `P` because j is actually `P.length` + // e.g. + // S = a b a b d e + // P = `a b`a b + // we will jump to `a b` and we will compare d and a in the next iteration + // a b a b `d` e + // a b `a` b + if (j === word.length) { + matches.push(i - j) + j = prefixes[j - 1] + // Case 3. + // S[i] != P[j] There's a mismatch! + } else if (str.charAt(i) !== word.charAt(j)) { + // if we found at least a character in common, do the same thing as in case 2 + if (j !== 0) { + j = prefixes[j - 1] + } else { + // else j = 0, and we can move to the next character S[i+1] + i++ + } + } + } + + return matches +} + +console.log(stringSearch('Hello search the position of me', 'pos'))