0

So I'm doing interview practice questions and I came across this one: Given a string str and array of pairs that indicates which indices in the string can be swapped, return the lexicographically largest string that results from doing the allowed swaps. You can swap indices any number of times.

Example

For str = "abdc" and pairs = [[1, 4], [3, 4]], the output should be swapLexOrder(str, pairs) = "dbca".

By swapping the given indices, you get the strings: "cbda", "cbad", "dbac", "dbca". The lexicographically largest string in this list is "dbca".

I've got a working answer involving finding unions but my answer is too slow:

[time limit] 4000ms (js)
0 ≤ pairs.length ≤ 5000,
pairs[i].length = 2.
1 ≤ str.length ≤ 10^4

Could someone help me tweak my code to increase its speed? Heres the code I have:

  function swapLexOrder(str, pairs) {
    if (!pairs.length){
        return str
    }

    answerHash = {}
    unmoved = findUnmoved(pairs, str.length)
    unionsArr = findUnions(pairs)


    for (i in unmoved){
        answerHash[unmoved[i]] = str[(unmoved[i]-1)]
    }

    unionsArr.forEach(function(union){
        letters = []
        for (i in union){
            letters.push(str[(union[i]-1)])
        }

        letters.sort()
        union.sort(function(a,b){
            return b-a
        })

        for (j in union){
            answerHash[union[j]] = letters[j]
        }
     })

    string = []
    for (keys in answerHash){
        string.push(answerHash[keys])
    }
    return string.join('')
}



//if two pairs share a number they belong in the same array
findUnions = function(pairs, unions){
   if (!unions){
       unions = [pairs[0]];
       pairs.shift();
   }else{
       if(pairs.length){
           unions.push(pairs[0])
           pairs.shift()
       }
   }

    if (!pairs.length){
        return unions
    }

    unite = true
    while (unite && pairs.length){
        unite = false
        loop1:
        for (i in unions){
            loop2:
            for (j in pairs){
                if (unions[i].includes(pairs[j][0])){
                    unions[i].push(pairs[j][1])
                    pairs.splice(j, 1)
                    unite = true
                    break loop1
                }else if (unions[i].includes(pairs[j][1])){
                    unions[i].push(pairs[j][0])
                    pairs.splice(j, 1)
                    unite = true
                    break loop1
                }
            }
        }
    }
    return findUnions(pairs, unions)
}


findUnmoved = function(pairs, length){
    range = []
    for (var i=1;i<length+1;i++){
        range.push(i);
    }
    allNum = [].concat.apply([], pairs)
    range = range.filter(function(x){
        return (!allNum.includes(x))
    })
    return range
}

Its probably the function I'm using to find the unions but I was thinking maybe I could do it without creating a hash? Also if ya'll know of a better method of solving the problem I'm always up for learning somethinig new. Thanks!

Stephen Agwu
  • 1,013
  • 2
  • 15
  • 29

1 Answers1

0

This one works faster.

function swapLexOrder(str, pairs) {
//Turn pairs into edge lists: O(n+m)
var graph = new Array(str.length).fill(0).map(e=>[]);
for(var pair of pairs) {
    graph[pair[0]-1].push(pair[1]-1);
    graph[pair[1]-1].push(pair[0]-1);
}

//Build all the ccs with dfs: O(n+m)
var ccs = [], ccnum = 0;
for(var c in str) {
    if(ccs[c])
        continue;
    ccs[c] = ++ccnum;
    var dfs = [...graph[c]];
    while(dfs.length) {
        var d = dfs.shift();
        if(ccs[d])
            continue;
        ccs[d] = ccnum;
        dfs.push(...graph[d]);
    }
}

//Group words by ccs: O(n)
var ccWords = new Array(ccnum).fill(0).map(e=>[]);
for(var c in str) {
    ccWords[ccs[c]-1].push(str[c]);
}

//Sort all words: O(n log n)
ccWords.map(e=>e.sort());

//Build the new string: O(n)
var output = "";
for(var c in str) {output += ccWords[ccs[c]-1].pop(); }
    return output;
}
Ralic Lo
  • 1
  • 2