How to solve one of this CodingBat java method? - arrays

Here is my homework:
We'll say that 2 strings "match" if they are non-empty and their first chars are the same. Loop over and then return the given array of non-empty strings as follows: if a string matches an earlier string in the array, swap the 2 strings in the array. When a position in the array has been swapped, it no longer matches anything. Using a map, this can be solved making just one pass over the array.
allSwap(["ab", "ac"]) → ["ac", "ab"]
allSwap(["ax", "bx", "cx", "cy", "by", "ay", "aaa", "azz"]) → ["ay", "by", "cy", "cx", "bx", "ax", "azz", "aaa"]
allSwap(["ax", "bx", "ay", "by", "ai", "aj", "bx", "by"]) → ["ay", "by", "ax", "bx", "aj", "ai", "by", "bx"]

In the map, store the first letter as the key, and the most recent index of the key as the value. When a letter doesn't exist in the map, add it to the map. When a letter already exists in the map, remove it from the map and swap with that index.
/**
* Swaps strings in the array that have the same first letter,
* reading left to right. Once a string has been swapped,
* it will not be swapped again. The input array will be mutated.
*
* #param strings the strings to perform swaps from
* #return the strings after swapping
*/
public static String[] allSwap(final String[] strings) {
// map of first characters, and the index where they were last seen
final Map<Character, Integer> potentialSwap = new HashMap<>();
for (int thisIndex = 0; thisIndex < strings.length; thisIndex++) {
if (strings[thisIndex].isEmpty()) {
continue; // skip empty strings
}
final Character firstChar = strings[thisIndex].charAt(0); // box charAt(0)
// remove firstChar from the map. If it's not found, returns null
final Integer potentialIndex = potentialSwap.remove(firstChar);
if (potentialIndex != null) {
final int thatIndex = potentialIndex; // unbox potentialIndex
// swap values at thisIndex and thatIndex
final String temp = strings[thatIndex];
strings[thatIndex] = strings[thisIndex];
strings[thisIndex] = temp;
} else {
// save the index for possible swapping later
potentialSwap.put(firstChar, thisIndex); // box thisIndex
}
}
return strings;
}
Ideone Demo

public String[] allSwap(String[] strings) {
// map of first characters, and the index where they were last seen
Map<Character, Integer> map = new HashMap();
for (int i = 0; i < strings.length; i++) {
char firstChar = strings[i].charAt(0);
// box charAt(0)
// remove firstChar from the map. If it's not found, returns null
Integer removedIndex = map.remove(firstChar);
if (removedIndex != null) {
int j = removedIndex;
// unbox potentialIndex
// swap values at thisIndex and thatIndex
String temp = strings[j];
strings[j] = strings[i];
strings[i] = temp;
} else {
// save the index for possible swapping later
map.put(firstChar, i);
}
}
return strings;
}

This can also work !
public String[] allSwap(String[] strings) {
Map<String,Integer> map = new HashMap<String,Integer>();
for(int i=0;i<strings.length;i++){
if(!map.containsKey(strings[i].substring(0,1))){
map.put(strings[i].substring(0,1),i);
}
else{
String temp = strings[map.get(strings[i].substring(0,1))];
strings[map.get(strings[i].substring(0,1))] = strings[i];
strings[i] = temp;
map.remove(strings[i].substring(0,1));
}
}
return strings;
}

Related

Deleting Elements in Array equal to a value

I'm trying to make a code that removes all the elements from the array that are equal to a given value. For example an array = [hi, hello, hi, bye] value = hi, it would given an output hello bye
Here's my code:
int count = 0;
for(int i=0; i<stringArr.length;
if(stringArr[i].equals(value)){
count--;
for(int j= i; j<stringArr.length-1; j++){
stringArr[j] = stringArr[j+1];
}
i--;
}
}
Problem is instead of the expected output as: hello bye
It gives an output of:
hello hi bye bye
Try Java stream api:
String value = "hi";
String[] stringArr = new String[] {"hi", "hello", "hi", "bye"};
String[] results = Arrays.stream(stringArr)
.filter(it -> !it.equalsIgnoreCase(value))
.toArray(String[]::new);
The problem is that you are shifting the values left but not decrementing the length of the array in the outer for.
Assign stringArr.length to count and use it in the for.
Trading memory for speed, you could create a new array of the same length and only add in the first occurrence of what you want.
String[] removeEqual(String[]array,String val){
boolean found =false;
String[]out=new String[array.length];
int count=0;
for (int i=0;i<array.length;i++){
if(array[i].equals(val)){
if(!found){
out[count++]=val;
found=true;
}
else out[count++]=val;
}
}
return Arrays.copyOf(out, count);
}
You may like to consider separate functions for separate conditions such as removeLessThan and removeGreaterThan to keep it functionally coherent.
I don't recommend to do any manipulation to the original array, create a new one. Recycling is good for the planet, but may be very harmful in code. So if you want to stick to the Arrays only, then create a new array with and add all elements you want into the new array, but I think you can do better. Your approach is very "C" like, this is Java, you have a lot of better tools, than arrays.
One of them are streams and lambdas like this
#Test
public void example_lambdas() {
String[] array = {"hi", "hello", "hi", "bye"};
String[] result = Arrays.stream(array).filter(element -> !"hi".equals(element)).toArray(String[]::new);
System.out.println(Arrays.toString(result));
}
Another option is to use list
#Test
public void example_list() {
String[] array = {"hi", "hello", "hi", "bye"};
List<String> list = new ArrayList<>(Arrays.asList(array));
Set<String> toBeRemoved = Collections.singleton("hi");
list.removeAll(toBeRemoved);
String[] result = list.toArray(new String[0]);
System.out.println(Arrays.toString(result));
}
An array has a fixed size that cannot be changed. Hence your result cannot be a two element array when you start with a four element array. If you want the result to be a two element array, then you will need to create a second array. If, however, you want the result to stay in the original array, then I suggest setting the excess array elements to null. The following code demonstrates.
String[] stringArr = {"hi", "hello", "hi", "bye"};
String condition = "==";
String value = "hi";
int count = stringArr.length;
for (int i = 0; i < count; i++) {
if (condition.equals("==")) {
if (stringArr[i].equals(value)) {
count--;
for (int j = i; j < stringArr.length - 1; j++) {
stringArr[j] = stringArr[j + 1];
}
stringArr[stringArr.length - 1] = null;
}
}
}
System.out.println(java.util.Arrays.toString(stringArr));
The above code prints the following:
[hello, bye, null, null]
EDIT
As requested, below code creates a new array that only contains the requested elements, i.e. the ones that were not removed from the original array.
String[] stringArr = {"hi", "hello", "hi", "bye"};
String condition = "==";
String value = "hi";
String[] temp = new String[stringArr.length];
int count = 0;
for (int i = 0; i < stringArr.length; i++) {
if (condition.equals("==")) {
if (!stringArr[i].equals(value)) {
temp[count++] = stringArr[i];
}
}
}
String[] result = new String[count];
for (int i = 0; i < count; i++) {
result[i] = temp[i];
}
System.out.println(Arrays.toString(result));
The above code prints the following:
[hello, bye]
In other words, result is a two element array.
Note that I assume that you only want to do array manipulation and you don't want to use classes in the JDK that most of the other answers have used.

Get index of Item in Loop to match against stored Indices

I have a scenario where I want to perform an action if a string in a list of string matches the index in a list of ints (Generated based on String list )
The below is some pseudo code to try and articulate what I am trying to achieve.
List<int> wordIndex = [1,3,5];
List<String> wordList = ['this', 'is','a', 'test', 'a'];
//Pseudo code
wordList.forEach(word)) {
if (wordIndex item matches index of word) {
do something;
} else {
od something else;
}
}
it the if (wordIndex item matches index of word) where I am having a problem and would appreciate any ideas.
Just use for instead of forEach;
List<int> wordIndex = [1,3,5];
List<String> wordList = ['this', 'is','a', 'test', 'a'];
//Pseudo code
for (int i = 0; i < wordList.length; i++) {
if (wordIndex.contains(i)) {
do something;
} else {
od something else;
}
}
If I understand you correctly, you want to know if the index of a word is contained in you wordIndex list, i.e. you want to get all wordList items with an index that is stored in wordIndex.
There are two approaches to this:
Use Iterable.contains
In this case, we are simple checking if the current index is present in the wordIndex list.
for (var index = 0; index < wordList.length; index++) {
if (wordIndex.contains(index)) {
// do something
return;
}
// do something else
}
Loop through wordIndex
If you are just interested in the matching items, this approach is more reasonable.
Here, we loop through the index list and then simply get the matching elements in the wordList. However, you will not be able to take actions for the non-matching items:
for (final index in wordIndex) {
final word = wordList[index];
// do something
}

identify recurring/duplicate patterns as sub-arrays from a parent array

I have a typical pattern searching problem where I need to identify where multiple patterns are appearing within an array and single them out.
ex: ['horse', 'camel', 'horse', 'camel', 'tiger', 'horse', 'camel', 'horse', 'camel']
function should return
['horse', 'camel'],
['horse', 'camel', 'horse'],
['camel', 'horse', 'camel'],
['horse', 'camel', 'horse', 'camel']
i.e. finding patterns that are repeating within an array which can become a sub-array,
Or the other way of defining is -> Find all the sub-arrays which are occurring more than 1 times in main array.
i.e. resulting arrays should have length > 1 ->
[1, 2, 3, 1, 2, 1, 4, 5] => [1,2,3] and [1,4,5] both are sub-arrays but [1,2,3] is recurring/repeating sub-array NOT [1,4,5]
Looking for a suitable efficient algorithm instead of brute-force looping solutions.
This probably isn't what you want but I don't know what you have tried yet so maybe it could be useful. Here's my direct approach which probably falls under your "brute-force looping solutions" but I figured give it a try since nobody has posted full answer.
In java:
// use this to not add duplicates to list
static boolean contains (List<String[]> patterns, String[] pattern){
for(String[] s: patterns)
if (Arrays.equals(pattern,s)) return true;
return false;
}
/**
*
* #param str String array containing all elements in your set
* #param start index of subarray
* #param end index of subarray
* #return if subarray is a recurring pattern
*/
static boolean search (String[] str,int start,int end) {
// length of pattern
int len = end - start + 1;
// how many times you want pattern to
// appear in text
int n = 1;
// increment m if pattern is matched
int m = 0;
// shift pattern down the array
for (int i = end+1; i <= str.length - len; i++) {
int j;
for (j = 0; j < len; j++) {
if (!str[i + j].equals(str[start + j]))
break;
}
// if pattern is matched at [i to i+len]
if (j == len) {
m++;
if (m == n) return true;
}
}
return false;
}
/**
*
* #param str String array containing all elements in your set
* #return a list of subsets of input set which are a recurring pattern
*/
static List<String[]> g (String[] str) {
// put patterns in here
List<String[]> patterns = new ArrayList<>();
// iterate through all possible subarrays in str
for(int i = 0; i < str.length-1; i++){
for(int j = i + 1; j < str.length; j++){
// if a pattern is found
if (search(str,i,j)) {
int len = j-i+1;
String[] subarray = new String[len];
System.arraycopy(str,i,subarray,0,len);
if (!contains(patterns,subarray))
patterns.add(subarray);
}
}
}
return patterns;
}
public static void main(String[] args) {
String[] str = {"horse", "camel", "horse", "camel", "tiger",
"horse", "camel", "horse", "camel"};
// print out
List<String[]> patterns = g(str);
for (String[] s: patterns)
System.out.println(Arrays.toString(s));
}
Output:
[horse, camel]
[horse, camel, horse]
[horse, camel, horse, camel]
[camel, horse]
[camel, horse, camel]
As mentioned in a comment i posted:
"would [camel, horse] be included in the output?"
The output I have goes with this as there are 2 instances of [camel, horse] at indices [1-2] and [6-7]. But maybe I am completely misunderstanding your question and I'm not understanding the constraints.
As for optimizing, the search(...) method for example is just a simple substring search there are some more optimized ways of doing this e.g. Knuth–Morris–Pratt. Sorry if this was exactly what you didn't want but maybe there's some use

Contiguous substrings containing one or two characters odf a given Ternary string

One question for an Interview was that : given a ternary string , find the number of contiguous substrings which contain only one or two character of the given ternary string. A ternary string is one which is made up of at most 3 characters. Like for ex: bcabb is a ternary string over the set {a,b,c}.
Ans to the above example will be: b,c,a,b,b,bc,ca,ab,bb ie.,9.
Note: substrings are decided by there start and end index not uniqueness.
Can anyone tell me what algo to follow in this question.
I couldn't fully understand what you are saying, but from the example and the description i think the answer will be 2 * strlen (string) - 1 . This is because you have strlen (string) number of single length string, and strlen (string) number of 2 length substring from the given string.
This question will require a suffix tree. Once you build the tree, you will traverse it in level order. Following code does the trick. It is in Java:
package org.algocode;
import java.util.ArrayDeque;
import java.util.Queue;
public class TernaryStringCombinator {
private static final int MAX_SIZE = 10000;
public static void main(String[] args) throws Exception {
// Read the input string.
String str = "abac";
if (str == null || str.length() > MAX_SIZE)
throw new Exception(
"Error! Input string is either null or greater than maximum allowed "
+ MAX_SIZE);
// Create the suffix tree
SuffixTree st = new SuffixTree(str);
st.levelOrderTraverse();
// You deduct one here because you don't want to count the root
// placeholder node.
System.out.println("Total nodes in tree " + (st.size() - 1));
// You deduct one here because you don't want to count the original
// string.
System.out.println("Total substrings with only one or two chars "
+ st.size2char());
}
/*
* A suffix tree is a tree of all possible contagious substrings of a
* string. It is a kind of a Trie. For example, for a given input string,
* ABBCD, the tree will store: ABBCD BBCD BCD CD D
*/
private static class SuffixTree {
private Node root;
private String s;
private int size;
private int size2char;
SuffixTree(String s_) throws Exception {
s = s_.toLowerCase();
size2char = s.length();
for (int i = 0; i < s.length(); i++)
insert(s.substring(i));
}
private void insert(String value) throws Exception {
if (root == null) {
root = new Node();
size++;
}
insert(root, value, 0);
}
// This is a recurrsive call to do the insertion
private void insert(Node node, String value, int index)
throws Exception {
Node next = null;
switch (value.charAt(index)) {
case 'a':
if (node.getA() == null)
createChildLink(node, value.charAt(index));
next = node.getA();
break;
case 'b':
if (node.getB() == null)
createChildLink(node, value.charAt(index));
next = node.getB();
break;
case 'c':
if (node.getC() == null)
createChildLink(node, value.charAt(index));
next = node.getC();
break;
default:
throw new Exception("Error! Character is not permitted. "
+ value);
}
if (index < (value.length() - 1)) {
insert(next, value.substring(index + 1), 0);
}
}
void levelOrderTraverse() {
if (root == null || root.isLeaf())
return;
Queue<Node> q = new ArrayDeque<Node>();
if (root.getA() != null)
q.add(root.getA());
if (root.getB() != null)
q.add(root.getB());
if (root.getC() != null)
q.add(root.getC());
while (!q.isEmpty()) {
Node n = (Node) q.poll();
// Only show if path has a color of 0 (two characters). Also the original
// string is not counted as a substring.
if (n.color() == 0) {
if (n.myPath().length() > 1 && !n.myPath().equalsIgnoreCase(s)) {
System.out.println("Two or more char path = "
+ n.myPath());
size2char++;
}
}
if (n.getA() != null)
q.add(n.getA());
if (n.getB() != null)
q.add(n.getB());
if (n.getC() != null)
q.add(n.getC());
}
}
Node root() {
return root;
}
int size() {
return size;
}
int size2char() {
return size2char;
}
private void createChildLink(Node parent, char childVal)
throws Exception {
Node child = new Node(parent, childVal);
size++;
switch (childVal) {
case 'a':
parent.setA(child);
break;
case 'b':
parent.setB(child);
break;
case 'c':
parent.setC(child);
break;
default:
throw new Exception("Error! Character is not permitted. "
+ childVal);
}
}
}
/*
* We will define an inner class to store a suffix tree node. Since it is a
* ternary string, we will have three child nodes of each string.
*/
private static class Node {
private Node parent;
private Node aLink;
private Node bLink;
private Node cLink;
private char value;
private String path;
// Color of a path. 0 if only one or two chars. 1 if all three are
// present in path.
private int color = 0;
Node(Node parent_, char value_) {
value = value_;
parent = parent_;
// Eagerly insert path
path = getPath();
// Eagerly calculate color. If my parent has a 1, then I will
// also be 1. If my parent has 0, then addition of myself can create
// my color to 0. This means that if my parent's path already has
// three
// characters, then I would be on a three character path.
if (parent.color() == 1)
color = 1;
else
colormyself();
}
Node() {
};
void setA(Node aLink_) {
this.aLink = aLink_;
}
void setB(Node bLink_) {
this.bLink = bLink_;
}
void setC(Node cLink_) {
this.cLink = cLink_;
}
Node getParent() {
return parent;
}
Node getA() {
return aLink;
}
Node getB() {
return bLink;
}
Node getC() {
return cLink;
}
char getValue() {
return value;
}
int color() {
return color;
}
// A special method to return combined string of parent
private String getPath() {
if (parent == null)
return null;
String path = parent.myPath();
if (path == null)
return String.valueOf(value);
StringBuilder stb = new StringBuilder();
stb.append(path);
stb.append(value);
return stb.toString();
}
String myPath() {
return path;
}
boolean isLeaf() {
return aLink == null && bLink == null && cLink == null;
}
private void colormyself() {
boolean sawA = false;
boolean sawB = false;
boolean sawC = false;
for (char c : path.toCharArray()) {
switch (c) {
case 'a':
sawA = true;
break;
case 'b':
sawB = true;
break;
case 'c':
sawC = true;
break;
}
if (sawA && sawB && sawC) {
color = 1;
break;
}
}
}
}
}
assume that the length of your string is n
So the result will be : 2*n-1
Iterate over the input string, considering the characters one by one. At each iteration, have 10 counters that count different types of substrings. Suppose you are considering a position p; then the different types of substrings are:
Those that end before position p; i call it result, because when the algorithm stops, it will contain the answer
Those that end at position p or later, and contain only 'a' characters; called aa
Those that end at position p or later, and contain only 'a' and 'b' characters; called ab
Similar to the above; called ac
Similar to the above; called ba
Similar to the above; called bb
Similar to the above; called bc
Similar to the above; called ca
Similar to the above; called cb
Similar to the above; called cc
It is easy to update each counter from position p to p+1, for example, if the character at position p is 'a', then:
Add all the counters to result, to account for all substrings that end at p
It's possible to continue a streak 'aaaaa' to 'aaaaaa', so increase the counter aa
It's possible to append 'a' to any substring of type 'ba'
It's possible to append 'a' to any substring of type 'ab', making it into 'ba', so add the counter ab to ba
It's possible to append 'a' to any substring of type 'b', making it into 'ba', so add the counter b to ba
It's impossible to append 'a' to any other substrings, so set all other counters to zero
Code fragment:
...
int result = 0;
int counters[3][3] = {{0}}; // [0][0] is aa; [0][1] is ab, etc
int L, x, y; // L represents the current Letter; x and y represent other letters
int p; // position in input string
for (p = 0; ; p++)
{
for (y = 0; y < 3; y++)
for (x = 0; x < 3; x++)
result += counters[y][x];
switch (str[p])
{
case 'a': L = 0; x = 1; y = 2; break;
case 'b': L = 1; x = 2; y = 0; break;
case 'c': L = 2; x = 0; y = 1; break;
case '\0': return result;
default: abort();
}
counters[L][L] += 1;
counters[x][L] += counters[x][x] + counters[L][x];
counters[y][L] += counters[y][y] + counters[L][y];
counters[L][x] = 0;
counters[x][x] = 0;
counters[y][x] = 0;
counters[L][y] = 0;
counters[x][y] = 0;
counters[y][y] = 0;
}
Oddly enough, this code outputs 10 (instead of 9) for string bcabb and 18 (instead of 17) for string abbaccb. I leave it as a fun exercise for you to discover which substring is missing...
static void ternaryStringSubstring(String A){
System.out.println("input ternary string :"+A);
int output = 0;
for(int i=0;i<A.length();i++){
String newStr = "";
for(int j=i+1;j<=A.length();j++){
newStr = A.substring(i, j);
boolean isTernary = true;// Keep track of long loop
List<String> l = new ArrayList<String>();
for(int k=0;k<newStr.length();k++){
String sew = newStr.substring(k, k+1);
if(!l.contains(sew)){
l.add(sew);
}
if(l.size()>2){
isTernary = false;//In case its a long string, it would break
break;
}
}
if(isTernary){
output = output+1;
}
}
}
System.out.println("output :"+output);
}
Let me know for more optimized solution
First, as I can see that you are printing duplicate elements. I am assuming that the size of string is 'n'. So you have to print n elements no matter if they are repeated. Same hold for two consecutive elements, so take 'n-1' for that. The total till now is '2n-1'. Now search for 2 consecutive elements and add "+2" for that in total (provided the preceding and succeeding characters are not null). Now do search for 3 consecutive elements and add "+3" for that in total. Repeat this till 'n' as there can be 'n' duplicate characters. Add them all. Hope this helps.

Generating All Permutations of Character Combinations when # of arrays and length of each array are unknown

I'm not sure how to ask my question in a succinct way, so I'll start with examples and expand from there. I am working with VBA, but I think this problem is non language specific and would only require a bright mind that can provide a pseudo code framework. Thanks in advance for the help!
Example:
I have 3 Character Arrays Like So:
Arr_1 = [X,Y,Z]
Arr_2 = [A,B]
Arr_3 = [1,2,3,4]
I would like to generate ALL possible permutations of the character arrays like so:
XA1
XA2
XA3
XA4
XB1
XB2
XB3
XB4
YA1
YA2
.
.
.
ZB3
ZB4
This can be easily solved using 3 while loops or for loops. My question is how do I solve for this if the # of arrays is unknown and the length of each array is unknown?
So as an example with 4 character arrays:
Arr_1 = [X,Y,Z]
Arr_2 = [A,B]
Arr_3 = [1,2,3,4]
Arr_4 = [a,b]
I would need to generate:
XA1a
XA1b
XA2a
XA2b
XA3a
XA3b
XA4a
XA4b
.
.
.
ZB4a
ZB4b
So the Generalized Example would be:
Arr_1 = [...]
Arr_2 = [...]
Arr_3 = [...]
.
.
.
Arr_x = [...]
Is there a way to structure a function that will generate an unknown number of loops and loop through the length of each array to generate the permutations? Or maybe there's a better way to think about the problem?
Thanks Everyone!
Recursive solution
This is actually the easiest, most straightforward solution. The following is in Java, but it should be instructive:
public class Main {
public static void main(String[] args) {
Object[][] arrs = {
{ "X", "Y", "Z" },
{ "A", "B" },
{ "1", "2" },
};
recurse("", arrs, 0);
}
static void recurse (String s, Object[][] arrs, int k) {
if (k == arrs.length) {
System.out.println(s);
} else {
for (Object o : arrs[k]) {
recurse(s + o, arrs, k + 1);
}
}
}
}
(see full output)
Note: Java arrays are 0-based, so k goes from 0..arrs.length-1 during the recursion, until k == arrs.length when it's the end of recursion.
Non-recursive solution
It's also possible to write a non-recursive solution, but frankly this is less intuitive. This is actually very similar to base conversion, e.g. from decimal to hexadecimal; it's a generalized form where each position have their own set of values.
public class Main {
public static void main(String[] args) {
Object[][] arrs = {
{ "X", "Y", "Z" },
{ "A", "B" },
{ "1", "2" },
};
int N = 1;
for (Object[] arr : arrs) {
N = N * arr.length;
}
for (int v = 0; v < N; v++) {
System.out.println(decode(arrs, v));
}
}
static String decode(Object[][] arrs, int v) {
String s = "";
for (Object[] arr : arrs) {
int M = arr.length;
s = s + arr[v % M];
v = v / M;
}
return s;
}
}
(see full output)
This produces the tuplets in a different order. If you want to generate them in the same order as the recursive solution, then you iterate through arrs "backward" during decode as follows:
static String decode(Object[][] arrs, int v) {
String s = "";
for (int i = arrs.length - 1; i >= 0; i--) {
int Ni = arrs[i].length;
s = arrs[i][v % Ni] + s;
v = v / Ni;
}
return s;
}
(see full output)
Thanks to #polygenelubricants for the excellent solution.
Here is the Javascript equivalent:
var a=['0'];
var b=['Auto', 'Home'];
var c=['Good'];
var d=['Tommy', 'Hilfiger', '*'];
var attrs = [a, b, c, d];
function recurse (s, attrs, k) {
if(k==attrs.length) {
console.log(s);
} else {
for(var i=0; i<attrs[k].length;i++) {
recurse(s+attrs[k][i], attrs, k+1);
}
}
}
recurse('', attrs, 0);
EDIT: Here's a ruby solution. Its pretty much the same as my other solution below, but assumes your input character arrays are words: So you can type:
% perm.rb ruby is cool
~/bin/perm.rb
#!/usr/bin/env ruby
def perm(args)
peg = Hash[args.collect {|v| [v,0]}]
nperms= 1
args.each { |a| nperms *= a.length }
perms = Array.new(nperms, "")
nperms.times do |p|
args.each { |a| perms[p] += a[peg[a]] }
args.each do |a|
peg[a] += 1
break if peg[a] < a.length
peg[a] = 0
end
end
perms
end
puts perm ARGV
OLD - I have a script to do this in MEL, (Maya's Embedded Language) - I'll try to translate to something C like, but don't expect it to run without a bit of fixing;) It works in Maya though.
First - throw all the arrays together in one long array with delimiters. (I'll leave that to you - because in my system it rips the values out of a UI). So, this means the delimiters will be taking up extra slots: To use your sample data above:
string delimitedArray[] = {"X","Y","Z","|","A","B","|","1","2","3","4","|"};
Of course you can concatenate as many arrays as you like.
string[] getPerms( string delimitedArray[]) {
string result[];
string delimiter("|");
string compactArray[]; // will be the same as delimitedArray, but without the "|" delimiters
int arraySizes[]; // will hold number of vals for each array
int offsets[]; // offsets will holds the indices where each new array starts.
int counters[]; // the values that will increment in the following loops, like pegs in each array
int nPemutations = 1;
int arrSize, offset, nArrays;
// do a prepass to find some information about the structure, and to build the compact array
for (s in delimitedArray) {
if (s == delimiter) {
nPemutations *= arrSize; // arrSize will have been counting elements
arraySizes[nArrays] = arrSize;
counters[nArrays] = 0; // reset the counter
nArrays ++; // nArrays goes up every time we find a new array
offsets.append(offset - arrSize) ; //its here, at the end of an array that we store the offset of this array
arrSize=0;
} else { // its one of the elements, not a delimiter
compactArray.append(s);
arrSize++;
offset++;
}
}
// put a bail out here if you like
if( nPemutations > 256) error("too many permutations " + nPemutations+". max is 256");
// now figure out the permutations
for (p=0;p<nPemutations;p++) {
string perm ="";
// In each array at the position of that array's counter
for (i=0;i<nArrays ;i++) {
int delimitedArrayIndex = counters[i] + offsets[i] ;
// build the string
perm += (compactArray[delimitedArrayIndex]);
}
result.append(perm);
// the interesting bit
// increment the array counters, but in fact the program
// will only get to increment a counter if the previous counter
// reached the end of its array, otherwise we break
for (i = 0; i < nArrays; ++i) {
counters[i] += 1;
if (counters[i] < arraySizes[i])
break;
counters[i] = 0;
}
}
return result;
}
If I understand the question correctly, I think you could put all your arrays into another array, thereby creating a jagged array.
Then, loop through all the arrays in your jagged array creating all the permutations you need.
Does that make sense?
it sounds like you've almost got it figured out already.
What if you put in there one more array, call it, say ArrayHolder , that holds all of your unknown number of arrays of unknown length. Then, you just need another loop, no?

Resources