REACTO:

String Search

Prompt

Write a function to find the index of the first appearance of one string (the needle) inside of another (the haystack).

  • The function, indexOf, should take 2 arguments, both of which are strings: "needle" and "haystack"
  • If needle is in haystack, return the index of the first appearance of the string
  • If needle is not in haystack, return -1

Examples

indexOf('or', 'hello world'); // should return 7
indexOf('howdy', 'hello world'); // should return -1
indexOf('oox', 'ooboxoooxo'); // should return 6

Naive Approach

const indexOf = (needle, haystack) => {
  let counter = 0;
  let result = -1;
  
  for (let i = 0; i < haystack.length; i++){
    for (let j = 0; j < needle.length; j++){
      if (haystack[i + j] === needle[j]) {
        counter++;      
        if (j === 0) result = i;
        if (counter === needle.length) return result;
      } else {
        result = -1;   
      }
    }
  }  
  
  return result;
};

// Big O
// O(n*m) where n is haystack size and m is needle size

Naive Approach

const indexOf = (needle, haystack) => {
  let counter = 0;
  let result = -1;
  
  for (let i = 0; i < haystack.length; i++){
    // for each "ith" element of the haystack, loop over the needle and check if 
    // the element at haystack[i + j] matches needle[j]
    for (let j = 0; j < needle.length; j++){
      if (haystack[i + j] === needle[j]) {
        counter++;      
          // if the needle has a length of 1, result = index at which it matches in haystack
        if (j === 0) result = i;
          // if counter = length of the needle, then you know you've accounted for
          // all the letters in needle
        if (counter === needle.length) return result;

      } else {
        result = -1;    // if needle not found, return -1
      }
    }
  }  
  
  return result;
};

Optimized Approach

const indexOf2 = (needle, haystack) => {
  
  for (let i = 0; i <= haystack.length - needle.length; i++){
    for (let j = 0; j < needle.length; j++){
      if (haystack[i + j] !== needle[j]) break;
      if (j === needle.length-1) return i;
    }
  }  
  return -1;
};


// Big O
// O(n*m) where n is haystack size and m is needle size

Optimized Approach

const indexOf2 = (needle, haystack) => {
  
  // Loop through the haystack for only as long as the difference
  // in length between haystack and needle.
  // No point in looping over 'orld' in 'hello world' after the index of 'hello' has been found
  // No point in looping over 'orld' in 'hello world' after the index of 'howdy' has NOT been found
  for (let i = 0; i <= haystack.length - needle.length; i++){
    // 'i' will be 'constant' here, make the haystack
    // index dynamic by adding the needle index
    for (let j = 0; j < needle.length; j++){

      // if the haystack letter doesn't equal needle letter just break out of the loop
      // so that 'i' can point to the next starting letter in haystack
      if (haystack[i + j] !== needle[j]) break;
      
      // If we're at the last letter of the needle it must mean
      // all the other letters matched. Therefore return the 'constant' starting 'i'.
      if (j === needle.length-1) return i;
    }
  }  
  // if we reach here, it means the needle wasn't found in the haystack.
  return -1;
};

Interviewer Tips

  • Have your interviewee solve the problem "naively" and then optimize later
  • When optimizing, have them think about how they can minimize the number of iterations through each for-loop
    • don't need to continue looping through the entire haystack.length when needle.length is x long
    • break out of the for-loop when a haystack letter doesn't match a needle letter--don't need to keep looping over the rest of the needle string

String Search

By Fanny Jiang

String Search

  • 152