So I have a list:
a 25
b 18
c 18
d 18
e 14
f 14
g 12
... and so on
For each number that matches, I have to get every permutation of the identifier. The lists I would need from my example would be as follows:
abcdefg
abdcefg
acbdefg
acdbefg
adbcefg
adcbefg
abcdfeg
abdcfeg
acbdfeg
acdbfeg
adbcfeg
adcbfeg
Steps I am taking currently to solve the problem:
If I were to work through it, I would be adding the numbers to a list like this:
a
abcd
abdc
adbc
...
abcdef
abcdfe
abdcef
adbcfe
...
abcdefg
abcdfeg
My current code doesn't work as intended (generates only one list) and I'm not even sure how to begin writing a code that would continuously generate new lists, while also appending the old lists. I apologize for being lost, I'm in a data structures course right now and it all feels out of my familiarity zone (we just started discussing linked lists).
Note 1: allLists holds all the current lists (a, abcd, adcb,) and permutationList holds all the permutations of the list.
Note 2: I'm doing this through a boolean list, but used letters as an easy visual representation for what I'm trying to accomplish
Code where I expect the problem to be:
public static Boolean[] combine (int i, int j) {
int aLen = allLists.get(j).length;
int bLen = permutationList.get(i).length;
Boolean[] newList = new Boolean[aLen + bLen];
System.arraycopy(allLists.get(j), 0, newList, 0, aLen);
System.arraycopy(permutationList.get(i), 0, newList, aLen, bLen);
return newList;
}
public static void setAllLists() {
if(allLists.size() == 0) {
allLists.add(permutationList.get(0));
}
for(int i = 0; i < permutationList.size(); i++) {
for(int j = 0; j < allLists.size(); j++) {
Boolean[] newList = combine(i,j);
if(i == 0) {
allLists.set(j, newList);
}
else {
allLists.add(newList);
}
}
}
permutationList.clear();
}
Assumptions:
I don't know your input format but I have assumed that I have two lists alphabets
which have list of alphabets and values
which have list of corresponding values. Both are equal in length and values
are in decreasing order.
Here's my approach to the problem:
Map<Integer, List<String>> valuesToListMap = new TreeMap<>(Collections.reverseOrder());
. Why? Because that map will tell the substrings I would need to find permutations for (in your example it would be {a},{b,c,d},{e,f},{g}
and since the map is of type TreeMap
and the entries in it will be in reverse natural ordering (descending) order, I can ensure the entries in the map are in strictly decreasing order. Sample entry in my map given your input will be 18 -> {b,c,d}
valuesToListMap
. I will find permutations of every list. For our example the output would be {{a},{bcd,bdc,cbd,cdb,dbc,dcb},{ef,fe},{g}
. Lets call that collection permutationsList
permutationsList
two entries at a time and start appending each element of first entry with each element of second entry . For example, I will append each element of entry {a}
in permutationsList
with each element of entry {bcd,bdc,cbd,cdb,dbc,dcb}
. So my output after the merge will be {abcd,abdc,acbd,acdb,adbc,adcb}
Some notes:
Using TreeMap
ensures we don't have an output bcdaefg
because that is not in descending order of values.
My solution is below. I have not done any input validation or sanity checks but that should be easy to do:
public class PermutationWithinSubsection {
public static void main(String[] args) {
List<String> alphabets = Arrays.asList("a","b","c","d","e","f","g");
List<Integer> values = Arrays.asList(25,18,18,18,14,14,12);
// Step 1 start
Map<Integer, List<String>> valuesToListMap = new TreeMap<>(Collections.reverseOrder()); // Order the map in reverse order of values
for(int i=0; i<alphabets.size(); i++) {
if(valuesToListMap.containsKey(values.get(i))) {
List<String> temp = valuesToListMap.get(values.get(i));
temp.add(alphabets.get(i));
valuesToListMap.put(values.get(i),temp);
} else {
List<String> temp = new ArrayList<>();
temp.add(alphabets.get(i));
valuesToListMap.put(values.get(i), temp);
}
}
// Step 1 end
// Step 2 start
List<List<String>> permutationsList = new ArrayList<>();
for(List<String> ip: valuesToListMap.values()) {
List<String> result = new PermutationWithinSubsection().permute(ip);
permutationsList.add(result);
}
// Step 2 end
// // Step 3 start
int count = 1;
List<String> l1 = permutationsList.get(0);
List<String> r = new PermutationWithinSubsection().merge(l1, permutationsList, count);
// // Step 3 end
System.out.println("r = " + r);
}
/**
* Find permutations of input list using backtracking
* @param ip
* @return
*/
private List<String> permute(List<String> ip) {
List<String> list = new ArrayList<>();
backtrack(list, new ArrayList<>(), ip);
return list;
}
private void backtrack(List<String> list, ArrayList<String> tempList, List<String> nums) {
if(tempList.size() == nums.size()){
StringBuilder sb = new StringBuilder();
for(String s: tempList) {
sb.append(s);
}
list.add(sb.toString());
} else{
for(int i = 0; i < nums.size(); i++){
if(tempList.contains(nums.get(i))) continue; // element already exists, skip
tempList.add(nums.get(i));
backtrack(list, tempList, nums);
tempList.remove(tempList.size() - 1);
}
}
}
/**
* Keep on merging lists till we have merged all
* @param l1
* @param resultLists
* @param count
* @return
*/
private List<String> merge(List<String> l1, List<List<String>> resultLists, int count) {
if(count == resultLists.size()) {
return l1;
}
List<String> l2 = resultLists.get(count);
List<String> f = new ArrayList<>();
for(String s1: l1) {
for(String s2: l2) {
f.add(s1+s2);
}
}
return merge(f,resultLists,++count);
}
}
Output: r = [abcdefg, abcdfeg, abdcefg, abdcfeg, acbdefg, acbdfeg, acdbefg, acdbfeg, adbcefg, adbcfeg, adcbefg, adcbfeg]
Hope my explanation was clear. Let me know if you have any questions.