0

I am trying to count the number of permutations that do not contain consecutive letters. My code passes tests like 'aabb' (answer:8) and 'aab' (answer:2), but does not pass cases like 'abcdefa'(my answer: 2520; correct answer: 3600). Here's my code:

function permAlone(str) {

    var totalPerm = 1;
    var result = [];

    //assign the first letter
    for (var i = 0; i < str.length; i++) {
        var firstNum = str[i];
        var perm = firstNum;

        //create an array from the remaining letters in the string
        for (var k = 0; k < str.length; k++) {
            if (k !== i) {
                perm += str[k];
            }
        }

        //Permutations: get the last letter and change its position by -1;
        //Keep changing that letters's position by -1 until its index is 1;
        //Then, take the last letter again and do the same thing;
        //Keep doing the same thing until the total num of permutations of the number of items in the string -1 is reached (factorial of the number of items in the string -1 because we already established what the very first letter must be).

        var permArr = perm.split("");
        var j = permArr.length - 1;
        var patternsLeft = totalNumPatterns(perm.length - 1);

        while (patternsLeft > 0) {
            var to = j - 1;
            var subRes = permArr.move(j, to);
            console.log(subRes);

            if (noDoubleLettersPresent(subRes)) {
                result.push([subRes]);
            }

            j -= 1;
            if (j == 1) {
                j = perm.length - 1;
            }
            patternsLeft--;
        }
    }
    return result.length;
}

Array.prototype.move = function(from, to) {
    this.splice(to, 0, (this.splice(from, 1))[0]);
    return this.join("");
};

function totalNumPatterns(numOfRotatingItems) {
    var iter = 1;
    for (var q = numOfRotatingItems; q > 1; q--) {
        iter *= q;
    }
    return iter;
}

function noDoubleLettersPresent(str) {
    if (str.match(/(.)\1/g)) {
        return false;
    } else {
        return true;
    }
}

permAlone('abcdefa');
Mousey
  • 1,793
  • 15
  • 34
Autumn
  • 221
  • 3
  • 13

2 Answers2

3

I think the problem was your permutation algorithm; where did you get that from? I tried it with a different one (after Filip Nguyen, adapted from his answer to this question) and it returns 3600 as expected.

function permAlone(str) {
    var result = 0;
    var fact = [1];
    for (var i = 1; i <= str.length; i++) {
        fact[i] = i * fact[i - 1];
    }
    for (var i = 0; i < fact[str.length]; i++) {
        var perm = "";
        var temp = str;
        var code = i;
        for (var pos = str.length; pos > 0; pos--) {
            var sel = code / fact[pos - 1];
            perm += temp.charAt(sel);
            code = code % fact[pos - 1];
            temp = temp.substring(0, sel) + temp.substring(sel + 1);
        }
        console.log(perm);
        if (! perm.match(/(.)\1/g)) result++;
    }
    return result;
}

alert(permAlone('abcdefa'));

UPDATE: In response to a related question, I wrote an algorithm which doesn't just brute force all the permutations and then skips the ones with adjacent doubles, but uses a logical way to only generate the correct permutations. It's explained here: Permutations excluding repeated characters and expanded to include any number of repeats per character here: Generate all permutations of a list without adjacent equal elements

Community
  • 1
  • 1
  • That's just something I came up with when I was thinking about permutations. Do you know where I went wrong and why my algorithm works for some cases but not all cases? – Autumn Jul 19 '15 at 22:47
  • As I said, I think it's an odd/even string length thing. If you're looking for permutation algorithms, this one seems easy to implement in javascript too: http://arxiv.org/vc/cs/papers/0306/0306025v1.pdf – m69 ''snarky and unwelcoming'' Jul 19 '15 at 22:56
1

I agree with m69, the bug seems to be in how you are generating permutations. I got 3600 for 'abcdefa' by implementing a different algorithm for generating permutations. My solution is below. Since it uses recursion to generate the permutations the solution is not fast, however you may find the code easier to follow, if speed is not important.

The reason for having a separate function to generate the array index values in the permutations was to verify that the permutation code was working properly. Since there are duplicate values in the input strings it's harder to debug issues in the permutation algorithm.

// Simple helper function to compute all permutations of string indices
function permute_indices_helper(input) {
    var result = [];    
    if (input.length == 0) {
        return [[]];
    }
    for(var i = 0; i < input.length; i++) {
        var head = input.splice(i, 1)[0]; 
        var tails = permute_indices_helper(input);
        for (var j = 0; j < tails.length; j++) {
            tails[j].splice(0, 0, head);
            result.push(tails[j]);
        }
        input.splice(i, 0, head); // check
    }
    return result;
};

// Given an array length, generate all permutations of possible indices
// for array of that length.
// Example: permute_indices(2) generates:
// [[0,1,2], [0,2,1], [1,0,2], ... , [2, 0, 1]]
function permute_indices(array_length) {
    var result = [];
    for (var i = 0; i < array_length; i++) {
        result.push(i);
    }
    return permute_indices_helper(result);
}

// Rearrange letters of input string according to indices.
// Example: "car", [2, 1, 0] 
// returns: "rac"
function rearrange_string(str, indices) {
    var result = "";
    for (var i = 0; i < indices.length; i++) {
        var string_index = indices[i];
        result += str[string_index];
    }
    return result;
}

function permAlone(str) {
    var result = 0;
    var permutation_indices = permute_indices(str.length);
    for (var i = 0; i < permutation_indices.length; i++) {
        var permuted_string = rearrange_string(str, permutation_indices[i]);
        if (! permuted_string.match(/(.)\1/g)) result++;
    }
    return result;
}

You can see a working example on JSFiddle.

Julia Schwarz
  • 2,430
  • 1
  • 16
  • 25