mjeanroy.dev

Cassidoo Week #176

Here is the interview question of cassidoo newsletter #176:

You’re given a string of characters that are only 2s and 0s. Return the index of the first occurrence of “2020” without using the indexOf (or similar) function, and -1 if it’s not found in the string.

Example:

$ find2020(‘2220000202220020200’)
$ 14

For this question, I will first implement a very naïve solution which is easy to understand and to read:

const SUBSTRING = '2020';

function find2020(str) {
  if (!str || str.length < SUBSTRING.length) {
    return -1;
  }

  let i = 0;
  let j = 0;

  for (; i < str.length; ++i) {
    const c = str[i];
    if (c === SUBSTRING[j]) {
      j++;
    } else if (j > 0) {
      i -= j;
      j = 0;
    }

    if (j === SUBSTRING.length) {
      return i - (j - 1);
    }
  }

  return -1;
}

In this solution, I iterate over the input string and I compare characters by characters until I find an exact match. The trick here is if I find a mismatch, I have to go back in the input string to do the same comparisons again (and in this question, with a string containing only 2 or 0, this situation may happens a lot…).

The advantage of this solution is that it is easy to understand and really easy to implement. The drawbacks is that it is really inefficient. Searching a pattern efficiently in a string is a well known algorithm: the KMP algorithm.

I will not explain this algorithm in details, you can find a very good one here: https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/.

So, here is my implementation of the KMP algorithm:

function lps(subString) {
  const output = [];
  const n = subString.length;

  let i = 0;
  let j = -1;

  while (i !== n) {
    const c1 = subString[i];
    const c2 = j >= 0 ? subString[j] : '';

    if (c1 === c2) {
      output[i] = j + 1;
      j++;
      i++;
    } else if (j > 0) {
      j = output[j - 1];
    } else {
      output[i] = 0;
      i++;
      j = 0;
    }
  }

  return output;
}

function kmpSearch(input, subString, lpsTable) {
  let i = 0;
  let m = 0;

  const n = subString.length;
  const l = input.length;

  while (true) {
    if ((m + i) === l) {
      return -1;
    }

    const c1 = subString[i];
    const c2 = input[m + i];
    if (c1 === c2) {
      i = i + 1;
      if (i === n) {
        return m;
      }
    }

    else {
      const e = i === 0 ? -1 : lpsTable[i - 1];
      m = m + i - e;
      if (i > 0) {
        i = e;
      }
    }
  }
}

function find2020KMP(input) {
  if (!input || input.length < SUBSTRING.length) {
    return -1;
  }

  const lpsTable = lps(SUBSTRING);
  return kmpSearch(input, SUBSTRING, lpsTable);
}

It is far more complicated to implement, but it is really faster!