Optimizing String combinations in Java - java

I need to obtain all combinations of a String of length k from given elements. For example, for
char[] elements = {'a', 'b', 'c'}
k = 4
the output should be: aaaa, aaab, ..., cccc. Now I have the following code which gives proper results BUT isn't fast enough. What can I improve in my code?
public static ArrayList<String> printAllKLength(char[] elements, int nrElements, int patternLength) {
ArrayList<String> patternVariations = new ArrayList<String>();
patternVariations = printAllKLengthRec(elements, "", nrElements, patternLength, patternVariations);
return patternVariations;
}
public static ArrayList<String> printAllKLengthRec(char[] elements, String prefix, int nrElements, int patternLength, ArrayList<String> patternVariations) {
if (patternLength == 0) {
patternVariations.add(prefix);
//System.out.println(prefix);
return patternVariations;
}
for (int i = 0; i < nrElements; ++i) {
String newPrefix = prefix + elements[i];
printAllKLengthRec(elements, newPrefix, nrElements, patternLength - 1, patternVariations);
}
return patternVariations;
}
Thank you!

Try doing it without recursation ... see the explanation here.
recursion versus iteration
I hope this helps you

You can use dynamic programming approach(memorization). Something very similar:
http://www.geeksforgeeks.org/print-all-possible-combinations-of-r-elements-in-a-given-array-of-size-n/

Related

Generate permutations on list of strings using one character only

I am trying to generate permutations using list of strings taking one character one time.
Below is the code of input and output that I want.
Can we simply do it iteratively?. Also I am not finding exact method.
String[] lst = new String[]{"abc", "def", "ghi"}; //Given
String[] permutations = new String[]{ //To Generate
"adg", "adh", "adi",
"aeg", "aeh", "aei",
"afg", "afh", "afi",
"bdg", "bdh", "bdi",
"beg", "beh", "bei",
"bfg", "bfh", "bfi",
"cdg", "cdh", "cdi",
"ceg", "ceh", "cei",
"cfg", "cfh", "cfi",
};
Update: I am not looking just for the above example with list size=3. It can be of any size and each string may happen to be of different length.
For ex: list = [ "ab", "abc", "defghi", "x", "einsigl"]
In this answer I will walk through how I solved this problem to find an algorithm that works for an array of any length for words which can be any length and are not required to all be the same length.
I will first make a recursive solution, and then transorm it into an iterative one.
The easiest way to answer problems like this is to think of them recursively:
Generating all permutations of [] should return [""]
Generating all permutations of a non-empty list means, for each letter c in the first word in the list, return all permutations of the rest of the list with c prepended on the front.
This can be written in Java as follows:
public static List<String> generatePermutationsRecursiveSlow(String[] words) {
if (words.length == 0)
// base case
return Collections.singletonList("");
else {
// recursive case
// result list
ArrayList<String> permutations = new ArrayList<>();
// split array into item 0 and items [1..end]
String firstWord = words[0];
String[] otherWords = new String[words.length - 1];
System.arraycopy(words, 1, otherWords, 0, words.length - 1);
// recurse to find permutations for items [1..end]
List<String> otherWordsPermutations = generatePermutationsRecursiveSlow(otherWords);
// for each character in the first word
for (char c : firstWord.toCharArray()) {
// for each permutation from the recursive call's results
for (String otherWordsPermutation : otherWordsPermutations) {
// prepend this character onto the permutation and add it to the results
permutations.add(c + otherWordsPermutation);
}
}
return permutations;
}
}
Calling generatePermutationsRecursiveSlow(new String[0]) returns [""].
Calling generatePermutationsRecursiveSlow(new String[]{"cd"}) will cause the local c variable to be equal to 'c', and it will recurse with an empty array as the argument, making otherWordsPermutations equal to [""], so it will add 'c' + "" (which is "c") to the results, then it will do the same for 'd', adding "d" to the results.
Calling generatePermutationsRecursiveSlow(new String[]{"ab", "cd"}) will mean that when c is 'a', it will add to the results list 'a'+"c", then 'a'+"d", and whencis'b', it will add'b'+"c"and'b'+"d"`
A similar but better optimised version which works in the same way can be written like this:
public static List<String> generatePermutationsRecursive(String[] words) {
ArrayList<String> permutations = new ArrayList<>();
int wordLen = words.length;
generatePermutationsRecursive(words, permutations, new char[wordLen], 0);
return permutations;
}
public static void generatePermutationsRecursive(String[] words, ArrayList<String> permutations, char[] word, int i) {
if (i == word.length) {
// base case
permutations.add(new String(word));
} else {
for (int j = 0; j < words[i].length(); j++) {
// equivalent of prepending
word[i] = words[i].charAt(j);
// recurse
generatePermutationsRecursive(words, permutations, word, i + 1);
}
}
}
This is better optimised since it uses the word parameter to avoid the O(n) prepending to the string by instead modifying a character array. It also introduces the parameter i which is the effective start index of the array, making it possible to avoid copying parts of the input array.
This can be transformed into an iterative approach by tracking the variables that change between different recursive calls using a stack (in place of the call stack):
private static List<String> generatePermutationsIterative(String[] words) {
// in the recursive version, each recursive function call would have its own local copy of `i` and `j`
// simulate that here with 2 stacks
ArrayDeque<Integer> i_stack = new ArrayDeque<>(words.length);
ArrayDeque<Integer> j_stack = new ArrayDeque<>(words.length);
i_stack.add(0);
j_stack.add(0);
char[] word = new char[words.length];
ArrayList<String> permutations = new ArrayList<>();
while (!i_stack.isEmpty()) {
int i = i_stack.removeLast();
int j = j_stack.removeLast();
if (i == words.length) {
// base case
permutations.add(new String(word));
continue;
}
if (!(j < words[i].length())) {
// reached end of loop `for (int j = 0; j < words[i].length(); j++)`
continue;
}
// if not reached end of loop `for (int j = 0; j < words[i].length(); j++)` yet,
// then increment `j` and allow next iteration to happen
i_stack.add(i);
j_stack.add(j + 1);
word[i] = words[i].charAt(j);
// recurse
i_stack.add(i + 1);
j_stack.add(0);
}
return permutations;
}
Code here
As a sidenote, look how cool Haskell is with this 2-line solution to the problem here (admittedly its not iterative, but it should have tail-call optimisation, making it as fast as an iterative solution).
Here's one way to do it that should work for arbitrary number of words of arbitrary length (not including 0).
String[] lst = new String[] {
"abc",
"def",
"ghi"
};
int numWords = lst.length;
int wordlen = lst[0].length();
int numPerms = (int) Math.pow(wordlen, numWords);
char[][] perms = new char[numPerms][numWords];
char[][] chararr = Arrays.stream(lst)
.map(String::toCharArray)
.toArray(i -> new char[i][wordlen]);
for (int i = 0; i < numWords; i++) {
double permsLocal = Math.pow(wordlen, i + 1);
int numRepeats = (int) Math.ceil((numPerms / permsLocal));
int repeats = (int)(permsLocal / wordlen);
for (int x = 0; x < repeats; x++) {
char[] word = chararr[i];
for (int j = 0; j < wordlen; j++) {
char c = word[j];
for (int k = 0; k < numRepeats; k++) {
perms[(x * wordlen * numRepeats) + k + j * numRepeats][i] = c;
}
}
}
}
String[] permutations = Arrays.stream(perms)
.map(String::new)
.toArray(String[]::new);
Output:
[adg, adh, adi, aeg, aeh, aei, afg, afh, afi, bdg, bdh, bdi, beg, beh,
bei, bfg, bfh, bfi, cdg, cdh, cdi, ceg, ceh, cei, cfg, cfh, cfi]
Link to repl.it: https://repl.it/repls/BoilingExcitingAttributes
You can do it as follows:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
String[] lst = new String[] { "abc", "def", "ghi" };
List<String> list = new ArrayList<>();
for (char a : lst[0].toCharArray()) {
for (char b : lst[1].toCharArray()) {
for (char c : lst[2].toCharArray()) {
list.add(new String(new char[] { a, b, c }));
}
}
}
// Convert to array
String[] permutations = list.toArray(new String[0]);
// Display
System.out.println(Arrays.toString(permutations));
}
}
Output:
[adg, adh, adi, aeg, aeh, aei, afg, afh, afi, bdg, bdh, bdi, beg, beh, bei, bfg, bfh, bfi, cdg, cdh, cdi, ceg, ceh, cei, cfg, cfh, cfi]

Creating substrings array from a string

I have a string that has no spaces and I wanted to create an array that consists of the substrings of the word. For instance, let the string is stackoverflow The array should be like:
[sta, cko, ver, flo, w]
The code that I use is below and that does give me only the first item. Any help will be appreciated.
public static ArrayList<String> getWords(String s){
ArrayList<String> words = new ArrayList<String>();
for(int i=0;i<s.length(); i=i+3){
words.add(s.substring(i, 3));
}
return words;
}
There are two issues with your code. First, the "3" in
s.substring(i, 3)
means the 3rd index from the beginning of the string, not the 3rd index from i, so you'd want
s.substring(i, i + 3)
Second, if the string is shorter than i + 3, you'll get an exception. To solve this, you can use Math.min. It would look something like this:
public static ArrayList<String> getWords(String s){
ArrayList<String> words = new ArrayList<String>();
for(int i=0;i<s.length(); i=i+3){
words.add(s.substring(i, Math.min(i + 3, i.length())));
}
return words;
}
You need to pass i + 3 as the second parameter of the substring call (it takes begin and end indexes). Also, I would prefer to program to the List interface. You might use += instead of i = i + 3. And you need an else clause for when there aren't three letters in the String. Like,
public static List<String> getWords(String s) {
List<String> words = new ArrayList<>();
for (int i = 0; i < s.length(); i += 3) {
if (i + 3 < s.length()) {
words.add(s.substring(i, i + 3));
} else {
words.add(s.substring(i));
}
}
return words;
}
Then, for completeness, I tested it with a basic main method like
public static void main(String[] args) {
System.out.println(getWords("stackoverflow"));
}
Which outputs (as requested)
[sta, cko, ver, flo, w]
You are on the right track, but your substring should be (i, i+3) as the following:
public static ArrayList<String> getWords(String s){
ArrayList<String> words = new ArrayList<String>();
for(int i=0;i<s.length(); i+=3){
if (i+3 >= s.length())
words.add(s.substring(i));
else
words.add(s.substring(i, i+3));
}
return words;
You can ease it significantly by using guava's Splitter:
String f = "stackoverflow";
List<String> p = Splitter.fixedLength(3).splitToList(f);
System.out.println(p); // [sta, cko, ver, flo, w]

Finding every possible combination of elements in an array in Java

Suppose I have this array: [a,b,c,d] How would I go about finding every possible combination i.e ab,abc,abcd.....etc. This needs to include duplicates, so abcd is not the same as dcba
The purpose is to find all combinations and check if if can make the same combination from a different array. My initial attempt is:
for (int i = 0; i < values.length; i++) {
String cur = values[i];
for (int k = 0; k < values.length; k++) {
if (i != k) {
cur = cur.concat(values[k]);
System.out.println(cur);
}
}
}
Which give the output :
ab
abc
abcd
ba
bac
bacd
ca
cab
cabd
da
dab
dabc
which is obviously not correct
This is for a programming challenge I'm doing to try and improve so any suggestions of a faster solution would be helpful
Is this what you're looking for?
public static void main(String[] data) {
ArrayList<Character> chars = new ArrayList<>(4);
chars.add('a');
chars.add('b');
chars.add('c');
chars.add('d');
System.out.println(getPermutations("", chars));
}
private static ArrayList<String> getPermutations(String currentResult, ArrayList<Character> possibleChars) {
ArrayList<String> result = new ArrayList<>(possibleChars.size());
for (char append: possibleChars) {
String permutation = currentResult + append; //create a new string with an additional character
result.add(permutation); //add the permutation to the result
if (possibleChars.size() > 0) {
//make a new list with the appendable characters
ArrayList<Character> possibleCharsUpdated = (ArrayList)possibleChars.clone();
//from that list, exclude the character we just appended
possibleCharsUpdated.remove(new Character(append));
//merge the result of a recursive call of this method and the result we already had
result.addAll(getPermutations(permutation, possibleCharsUpdated));
}
}
return result;
}

4 digits numbers in an array as String

I need a String array with the following attributes:
4 digits numbers
No repeating digits ("1214" is invalid)
No 0's
Is there an easier way to do this than manually type it? Like:
String[] s = {"1234","1235",1236",1237",1238",1239","1243","1245"};
Sorry for my English!
The following code will generate an array with your specifications.
public class Test {
public static void main(String[] args) {
List<String> result = new ArrayList<>();
Set<Character> set = new HashSet<>();
for (int i = 1234; i <= 9876; i++) {
set.clear();
String iAsString = Integer.toString(i);
char[] chars = iAsString.toCharArray();
boolean valid = true;
for (char c : chars) {
if (c == '0' || !set.add(c)) {
valid = false;
break;
}
}
if (valid) {
result.add(iAsString);
}
}
String[] yourStringArray = result.toArray(new String[result.size()]);
System.out.println(Arrays.toString(yourStringArray));
}
}
****edit****
Just saw that it is in Java. So use this function: String.valueOf(number) to convert integer to string if none of the digits are repeats in the loop.
Not sure what language you are doing but I am assuming no repeats not replies.
So what you can do is have a loop from 0 to 9999 and then run through all the numbers while checking if each digit has repeats if so discard number (do not store it into array).
You can convert integers to strings in many languages in their function so you can do that then store it into array.
Good luck.
Hope this helped (fastest solution from my head...there could be more efficient ones)
Try this method for creating Random number with your structure :
ArrayList<Integer> rawNumbers = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9));
public String createRandomNumberSring()
{
String result = "";
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.addAll(rawNumbers);
for(int i = 0; i < 4; i++)
{
int index = (int)(Math.random() * (numbers.size() + 1));
result += numbers.get(index).toString();
numbers.remove(index);
}
return result;
}

Sorting a list of substrings by order they appear in original

I have used a oomparator to sort an array by the length of the substrings. It works well, but is there a way to order the substrings of the same length in the array, ideally in the order they appear in the original string? For example if I sort the substrings of banana I get:
banana, anana, banan, bana, nana, anan... in this order and would like to get: banana, banan, anana, bana, anan, nana... which is the way they originally appear in banana.
How array is filled:
public static String longestRepeated(String line) {
HashMap<String, Integer> subArray = new HashMap<String, Integer>();
String answer = "";
for (int i = 0; i < line.length(); i++) {
for(int j = 1 ; j <= line.length() - i ; j++) {
if (!subArray.containsKey(line.substring(i, i + j))) {
subArray.put(line.substring(i, i + j), i);
}
}
}
answer = subArray.keySet().toString();
answer = answer.replaceAll("\\[", "");
answer = answer.replaceAll("\\]", "");
String[] subs = answer.split(",");
LongestRepeatedSubstring lrs = new LongestRepeatedSubstring(line);
Arrays.sort(subs, lrs);
for (int i = 0 ; i < subs.length; i++) {
subs[i] = subs[i].trim();
}
comparator:
int lineLength;
public int compare(String str1, String str2) {
int dist1 = Math.abs(str1.length() - lineLength);
int dist2 = Math.abs(str2.length() - lineLength);
return dist1 - dist2;
}
public LongestRepeatedSubstring(String line) {
super();
this.lineLength = line.length();
}
Then I use some loops to find the first one that is repeated and not at the same index. The issue occurs on cases where there are multiple repeated substrings in the array of the same length. I need the one the appears in the String first and they appear in the array randomly.
I don't really get what you are asking but I think that what you are trying to say is that if there is a way to campare again inside the comparator. Right? If that is the case here is an example:
public static Comparator<String> CompareBySomeRestrictionAndOtherRestriction = new Comparator<String>(){
public int compare(String s, String p){
if(){//first condition, for example some strings are the same size
//then use some other criteria, for example alphabetical order
}else{
}
}
}
You just have to work your way in how you are managing the comparisons in order to achieve what you want.
It seems that you do not need a comparator: just print all the substrings

Categories