13

It's a programming puzzle which goes like: "A number is said to be brilliant if the product of all digits of its substrings have a unique value."

Example : 263 (2, 6, 3, 2*6 = 12, 6*3 = 18) is brilliant.

But 236 (2, 3, 6, 2*3 = 6, 3*6 = 18) is not brilliant.

We take only substrings, not subsequences.

I was thinking maybe we can apply Dynamic Programming here because of repeated product calculations? What other solutions can we have for it? (This isn't a homework question.)

h4ck3d
  • 6,134
  • 15
  • 51
  • 74
  • 2
    Does this check only strict subsets, or all subsets? In other words, do you check `2*6*3` in the first example? – murgatroid99 Aug 24 '12 at 18:40
  • Can you be a little more specific with your definition of colorful numbers? "only strict I think" isn't that specific. Can you add additional examples, along with calculations? – Wug Aug 24 '12 at 18:58
  • Inclusion of the non-strict substring could only matter for numbers with `0` or `1` in their decimal digits, which are already guaranteed to not be brilliant anyway if there are at least 3 digits in the number; otherwise the total product is necessarily greater than any sub-product. I think the only numbers where it matters are `x0`, `x1`, and `1x` for `x ≠ 0`, plus `10`. – Danica Aug 24 '12 at 19:42
  • For 263 , the substrings are , 2 ,6,3,26,63 . 263 is not taken into consideration. – h4ck3d Aug 24 '12 at 20:06
  • DP is really not necessary FWIW. Just enumerate all substrings with increasing left border. Time complexity is O(n^2), just like the DP solution. – Niklas B. Jul 14 '15 at 12:41

5 Answers5

6

Here's one way of solving it using dynamic programming:

Assume we have the number d0 d1 ... dN as input.

The idea is to create a table, where cell (i, j) store the product di · di+1 · ... · dj. This can be done efficiently since the cell at (i, j) can be computed by multiplying the number at (i-1, j) by di.

Since i (the start index) must be less than or equal to j (the end index), we'll focus on the lower left triangle of the table.

After generating the table, we check for duplicate entries.

Here's a concrete example solution for input 2673:

  1. We allocate a matrix, M, with dimensions 4 × 4.

    enter image description here

  2. We start by filling in the diagonals, Mi,i with di:

    enter image description here

  3. We then go row by row, and fill in Mi,j with di ·Mi-1,j

    enter image description here

  4. The result looks like

    enter image description here

  5. To check for duplicates, we collect the products (2, 12, 6, 84, 42, 7, 252, 126, 21, 3), sort them (2, 3, 6, 7, 12, 21, 42, 84, 126, 252), and loop through to see if two consecutive numbers are equal. If so we return false, otherwise true.

In Java code:

Here's a working DP solution, O(n2).

public static boolean isColorful(int num) {

    // Some initialization
    String str = "" + num;
    int[] digits = new int[str.length()];
    for (int i = 0; i < str.length(); i++)
        digits[i] = str.charAt(i) - '0';
    int[][] dpmatrix = new int[str.length()][str.length()];

    // Fill in diagonal: O(N)
    for (int i = 0; i < digits.length; i++)
        dpmatrix[i][i] = digits[i];

    // Fill in lower left triangle: O(N^2)
    for (int i = 0; i < str.length(); i++)
        for (int j = 0; j < i; j++)
            dpmatrix[i][j] = digits[i] * dpmatrix[i-1][j];

    // Check for dups: O(N^2)
    int[] nums = new int[digits.length * (digits.length+1) / 2];
    for (int i = 0, j = 0; i < digits.length; i++, j += i)
        System.arraycopy(dpmatrix[i], 0, nums, j, i+1);

    Arrays.sort(nums);

    for (int i = 0; i < nums.length - 1; i++)
        if (nums[i] == nums[i+1])
            return false;

    return true;
}

For DP-interested readers I can recommend the somewhat similar question/answer over here:

Community
  • 1
  • 1
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • Mind explaining the algorithm ? – h4ck3d Aug 24 '12 at 19:36
  • Note that since OP wants strict substrings only, the lower-left corner shouldn't be included. – Danica Aug 25 '12 at 17:34
  • 2
    No brilliant number is over 8 digits *(no digits can repeat, and there can be no `0` or `1`)*, meaning the naive solution requires a max of 64 iterations, while the dynamic programming solution requires a max of 36 iterations. Both of these are practically nothing to modern processors. – BlueRaja - Danny Pflughoeft Aug 27 '12 at 18:24
  • For base 10 yes. You could even unroll the loops or list all brilliant numbers in the code to get O(1). From a complexity standpoint I would assume that the base is fixed but arbitrary. That makes the problem slightly more interesting. – aioobe Aug 27 '12 at 21:31
  • @BlueRaja-DannyPflughoeft: hah that's pretty funny. a solution to something that isn't a problem... – Claudiu Aug 28 '12 at 17:22
3

Using dynamic programming is probably the way to go: Instead of calculating all O(n^2) substrings, and then using ~n multiplication commands to calculate each of them, store the results of previous caclulation in a matrix M, where M(i,j) is the result of the substring of length j, starting from position i.

(i.e, if your number is 123456789, then M(1,5) is 5!, and M(1,6) is 6!, which only requires multiplying M(1,5) by 6 - constant work)

This will improve the running time from O(n^3) for n digits to O(n^2).

Guy Adini
  • 5,188
  • 5
  • 32
  • 34
3

A dynamic programming solution is really not necessary, as there are no brilliant numbers with a large number of digits (if any digit appears more than once, the number is not brilliant).

Here is a list of every brilliant number. There are 57,281 total.

This file took less than a second to generate on my PC, even without using dynamic programming :)

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
0

if we don't consider the number as a large string then hashing can help;

 int brill(int A) {
    map<long long int,bool> m;
    vector<int> arr(10,0);
    int i=0;
    while(A){
        arr[i++]=A%10;
        A/=10;
    }

    for(int j=0;j<i;j++){

        long long int temp=1;

        for(int k=j;k>=0;k--){
            temp*=arr[k];

            if(m.find(temp)!=m.end()){
                return 0;
            }
            else{
                m[temp]=true;
            }
        }
    }
    return 1;

}
chandan
  • 964
  • 1
  • 19
  • 26
0

n is the string containing the number.

Since the number can't be greater than 8 digits before a failure state its O(1).

function isBrill(n) {
  var set = {};
  set[parseInt(n.charAt(0))] = true;
  for(var i=0; i < n.length - 1; i++) {
    var a = parseInt(n.charAt(i));
    var b = parseInt(n.charAt(i+1));
    if(set[b] === true) { return false; }
    set[b] = true;
    if(set[a * b] === true) { return false; }
    set[a * b] = true;
  }
  return true;
}
isBrill("263"); // true
isBrill("236"); // false
Louis Ricci
  • 20,804
  • 5
  • 48
  • 62