Programmer Strings

class CounterWithLen(object):
  def __init__(self, it):
    self.mem = {}
    self.length = len(it)
    for ch in it:
      self.mem[ch] = self.mem.get(ch, 0) + 1

  def deduct(self, ch):
    if ch in self.mem and self.mem[ch] > 0:
      self.mem[ch] = self.mem[ch]-1
      self.length -= 1

def programmer_ind(s, start, step):
  counter = CounterWithLen('programmer')
  i = start
  while len(s) > i >= 0:
    counter.deduct(s[i])
    if counter.length == 0:
      # completed programmer string here.
      return i
    i += step
  return -1

def programmer_strings(s):
  start = programmer_ind(s, 0, 1)
  end   = programmer_ind(s, len(s)-1, -1)

  if start == -1 or end == -1:
    # should be impossible... but just in case.
    return 0

  # it's guaranteed that a *non-overlapping* programmer string
  # occurs in s so we don't need to check whether end>start.
  return end-start-1

#Challenge

SourceLanguageRuntime
OApython\(\mathcal{O}(n)\)

Given a string s a programmer string t is a substring of s that contains all the characters in the string programmer. For example "aprogrammera" has a programmer string starting at index 1. "ammerprog" is also a programmer string (starting at index 0).

Given a string s that's guaranteed to have two none-overlapping programmer strings find the distance between these two programmer strings. For example "programmerprogrammer" has a distance of 0, "programmeraprogrammer" has a distance of 1.

NOTE: We only care about the distance between the two furthest apart programmer strings. "programmerprogrammerprogrammer" has a distance of 10, not 0.

#Solution

This problem could be more easily phrased as:

Have we found all the characters in a programmer string.

If we can guarantee that condition in linear time than finding the distance between two programmer strings becomes as straightforward as simply finding the first programmer string and the last programmer string and finding the distance between them.

To assert whether we've found a programmer string I've created a special counter class CounterWithLen. This class takes an input string and produces a dict mapping from character to frequencies. It also keeps track of the sum of all the frequencies currently in the Counter. In programmer_ind I start at one end of the string and for each character I encounter I decrement the same counter in CounterWithLen (avoiding any negative values). Once the sum of all the counts in the counter become 0 all the characters in the input string have been encountered (at least as many times as they occur in that string). This gives me a quick way to find the index of a programmer string.

I then find these indexes from the start of the string and then end of it and then return the difference between them.

#Test Cases

programmer_strings('progxrammerrxproxgrammer') # 2
programmer_strings('programmerprogrammer') # 0
programmer_strings('xprogxrmaxemrppprmmograeiruu') # 2