Given a string of a million numbers, return all repeating 3 digit numbers Given a string of a million numbers, return all repeating 3 digit numbers python python

Given a string of a million numbers, return all repeating 3 digit numbers


You got off lightly, you probably don't want to be working for a hedge fund where the quants don't understand basic algorithms :-)

There is no way to process an arbitrarily-sized data structure in O(1) if, as in this case, you need to visit every element at least once. The best you can hope for is O(n) in this case, where n is the length of the string.

Although, as an aside, a nominal O(n) algorithm will be O(1) for a fixed input size so, technically, they may have been correct here. However, that's not usually how people use complexity analysis.

It appears to me you could have impressed them in a number of ways.

First, by informing them that it's not possible to do it in O(1), unless you use the "suspect" reasoning given above.

Second, by showing your elite skills by providing Pythonic code such as:

inpStr = '123412345123456'# O(1) array creation.freq = [0] * 1000# O(n) string processing.for val in [int(inpStr[pos:pos+3]) for pos in range(len(inpStr) - 2)]:    freq[val] += 1# O(1) output of relevant array values.print ([(num, freq[num]) for num in range(1000) if freq[num] > 1])

This outputs:

[(123, 3), (234, 3), (345, 2)]

though you could, of course, modify the output format to anything you desire.

And, finally, by telling them there's almost certainly no problem with an O(n) solution, since the code above delivers results for a one-million-digit string in well under half a second. It seems to scale quite linearly as well, since a 10,000,000-character string takes 3.5 seconds and a 100,000,000-character one takes 36 seconds.

And, if they need better than that, there are ways to parallelise this sort of stuff that can greatly speed it up.

Not within a single Python interpreter of course, due to the GIL, but you could split the string into something like (overlap indicated by vv is required to allow proper processing of the boundary areas):

    vv123412  vv    123451        5123456

You can farm these out to separate workers and combine the results afterwards.

The splitting of input and combining of output are likely to swamp any saving with small strings (and possibly even million-digit strings) but, for much larger data sets, it may well make a difference. My usual mantra of "measure, don't guess" applies here, of course.


This mantra also applies to other possibilities, such as bypassing Python altogether and using a different language which may be faster.

For example, the following C code, running on the same hardware as the earlier Python code, handles a hundred million digits in 0.6 seconds, roughly the same amount of time as the Python code processed one million. In other words, much faster:

#include <stdio.h>#include <string.h>int main(void) {    static char inpStr[100000000+1];    static int freq[1000];    // Set up test data.    memset(inpStr, '1', sizeof(inpStr));    inpStr[sizeof(inpStr)-1] = '\0';    // Need at least three digits to do anything useful.    if (strlen(inpStr) <= 2) return 0;    // Get initial feed from first two digits, process others.    int val = (inpStr[0] - '0') * 10 + inpStr[1] - '0';    char *inpPtr = &(inpStr[2]);    while (*inpPtr != '\0') {        // Remove hundreds, add next digit as units, adjust table.        val = (val % 100) * 10 + *inpPtr++ - '0';        freq[val]++;    }    // Output (relevant part of) table.    for (int i = 0; i < 1000; ++i)        if (freq[i] > 1)            printf("%3d -> %d\n", i, freq[i]);    return 0;}


Constant time isn't possible. All 1 million digits need to be looked at at least once, so that is a time complexity of O(n), where n = 1 million in this case.

For a simple O(n) solution, create an array of size 1000 that represents the number of occurrences of each possible 3 digit number. Advance 1 digit at a time, first index == 0, last index == 999997, and increment array[3 digit number] to create a histogram (count of occurrences for each possible 3 digit number). Then output the content of the array with counts > 1.


A million is small for the answer I give below. Expecting only that you have to be able to run the solution in the interview, without a pause, then The following works in less than two seconds and gives the required result:

from collections import Counterdef triple_counter(s):    c = Counter(s[n-3: n] for n in range(3, len(s)))    for tri, n in c.most_common():        if n > 1:            print('%s - %i times.' % (tri, n))        else:            breakif __name__ == '__main__':    import random    s = ''.join(random.choice('0123456789') for _ in range(1_000_000))    triple_counter(s)

Hopefully the interviewer would be looking for use of the standard libraries collections.Counter class.

Parallel execution version

I wrote a blog post on this with more explanation.