More efficient way to find all combinations? - java

Say you have a List of Strings or whatever, and you want to produce another List which will contain every possible combination of two strings from the original list (concated together), is there any more efficient way to do this other than using a nested for loop to combine the String with all the others?
Some sample code:
for(String s: bytes) {
for(String a: bytes) {
if(!(bytes.indexOf(a) == bytes.indexOf(s))) {
if(s.concat(a).length() == targetLength) {
String combination = s.concat(a);
validSolutions.add(combination);
}
}
}
}
The time for execution gets pretty bad pretty quickly as the size of the original list of Strings grows.
Any more efficient way to do this?

You can avoid checking i != j condition by setting j = i + 1. Also, things like bytes.length() get evaluated at each iteration of both loops - save it into a value and reuse. Calling a.length() inside the loop asks for a length of the same string multiple times - you can save some runtime on that as well. Here are the updates:
int len = bytes.length();
int aLength;
String a, b;
for(int i=0; i<len; i++) {
a = bytes[i];
aLength = a.length();
for(int j=i; j<len; j++) {
b = bytes[j];
if (b.length() + aLength == targetLength) {
validSolutions.add(b.concat(a));
validSolutions.add(a.concat(b));
}
}
}
Edit: j = i because you want to consider a combination of a string with itself; Also, you'd need to add a.concat(b) as well since this combination is never considered in the loop, but is a valid string

You can't get Better than O(N^2), because there are that many combinations. But you could speed up your algorithm a bit (from O(N^3)) by removing the indexOf calls:
for(int i=0; i<bytes.length(); i++) {
for(int j=0; j<bytes.length(); j++) {
string s = bytes[i];
string a = bytes[j];
if (i != j && s.length() + a.length() == targetLength) {
validSolutions.add(s.concat(a));
}
}
}

In addition to what Jimmy and lynxoid say, the fact that the total length is constrained gives you a further optimization. Sort your strings in order of length, then for each s you know that you require only the as such that a.length() == targetLength - s.length().
So as soon as you hit a string longer than that you can break out of the inner loop (since all the rest will be longer), and you can start at the "right" place for example with a lower-bound binary search into the array.
Complexity is still O(n^2), since in the worst case all the strings are the same length, equal to half of totalLength. Typically though it should go somewhat better than considering all pairs of strings.

Related

Finding number of sub strings between two giving chars

I need for my homework to write method that get two parameters, string and char, and it needs to return the number of strings that start with that char and end with that char.
Example:
For the string "abcbcabcacab" and the char 'c', the method will return 6.
(the sub strings are "cbc", "cabc", "cac", "cbcabc", "cabcac", "cbcabcac")
It needs to be as effectiveness as possible, and the only two method that I can use is charAt() and length().This is hard as hell and after 3 hours of trying, I'm asking you guys if there is someone who cal solve this or at least show me some clue.
The obvious solution is to check every symbol in input string and if equals to specified char, then try to find all possible substrings, starting from this point, like this:
long cnt = 0;
for (int i = 0; i < (s.length() - 1); i++)
if (s.charAt(i) == c)
for (int j = (i + 1); j < s.length(); j++)
if (s.charAt(j) == c)
cnt++;
return cnt;
this solution has complexity O(N^2)
but, number of substrings actually determined by number of occurrences of char in string, which appeared to be Triangular number
So, optimized O(N) solution is:
long cnt = -1;
for (int i = 0; i < s.length(); i++)
if (s.charAt(i) == c)
cnt++;
return (cnt * (cnt + 1)) >>> 1;
The simplest anwser is that you should use a loop.
Loop through the strings. Check if the current string starts with the given char and ends with the given char. If it doesn't just jump to the next one. if it does just loop with a counter from 1 to length minus the second last char.
You will reduce time by kicking out invalid string and you will be checking 2 chars less EVERY string ;)
I guess that's a quite fast way.
Example
// String definition bla String[] myArray
int count = 0;
for(int i = 0; i < myArray.length(); i++) {
if(myArray[i].charAt(0) == givenChar && myArray[i].charAt(myArray.length()-1)) {
for(int j = 1; j < myArray.length()-2;j++) {
if(myArray[i].charAt(j) == givenChar)
count ++;
}
}
}
That's the rough idea.
I am checking if the given String starts and ends with the givenChar.
If it does im iterating through the string checking for the givenChar again.
If it does exist i am counting 1 up.
I hope that might help your out ;)
Have a nice day!

sort a string in recursion

I have a certain string and I want to sort it in recursion.
My code is error free but the algorithm is not working and I need help
The index will be zero when calling the function.
The main idea is the compare between indexes in the string and creating a new string each time with the new sequence of the letters compared.
each call I send the new string which was created in each run
private static String sort(String s1, int index)
{
String s2="";
if (index == s1.length()-2)
return s1;
else
{
if (s1.charAt(index) > s1.charAt(index+1))
{
for (int i = 0; i < s1.length(); i++)
{
if (index == i)
{
s2 += s1.charAt(index+1);
s2 += s1.charAt(index);
i += 2;
}
s2 += s1.charAt(i);
}
}
else
{
for (int i = 0; i < s1.length(); i++)
{
if (index == i)
{
s2 += s1.charAt(index);
s2 += s1.charAt(index+1);
i += 2;
}
s2 += s1.charAt(i);
}
}
return (sort(s2,++index));
}
}
input : acbacds
output: abaccds
the output should be : aabccds
Each call compares a pair of adjacent characters; if they're out of order, you switch them.
Your recursion simply replaces an outer loop running through the length of the array.
The end of this process guarantees that the largest value will now be at the end of the array. To this extent, it works correctly.
If you expect an array of N elements to get fully sorted, you must repeat this process up to N-1 times. The only reason your given example is so close is that the array you gave it is already very close to sorted.
Try again with something in reverse order, and you'll see the effect. For instance, use "hgfedcba". One pass will get you "gfedcbah", moving the 'h' from the front to the end.
If you want a working bubble sort, try searching here on SO or on the web overall.
Finally, you might look into the Java substring functions; building s2 a character at a time is hard on the eyes; it's also slow, especially in the case where you don't switch characters.

How to find first instance of element array

I'm a pretty basic programmer and I'm coding a 'Master-mind' style guessing game program.
Now the part I'm stuck with is that I want to go through an array and increase the pointer when I come across a specific number.
Now thats pretty easy and stuff, but what I want to do is ONLY increase the counter if the number is encountered for the first time. So, for example if there are two numbers (189, 999), I want the counter to increase only once, instead of 3 times, which is what my code is doing. I know why its doing that, but I can't really figure out a way to NOT do it (except maybe declaring an array and putting all the repeated numbers in there and only incrementing it if none of the numbers match, but that's super inefficient) Here's my code:
for (int i = 0; i < mString.length(); i++) {
for (int j = 0; j < nString.length(); j++) {
if (mString.charAt(i) == nString.charAt(j)) {
correctNumbers++;
}
}
}
Thanks for taking the time to read! I'd prefer it if you wouldn't give me a direct answer and just point me in the right direction so I can learn better. Thanks again!
Your question is quite unclear. I suppose 989 and 999 will return 1. Because you only deal with number, so the solution is:
Create a boolean array with 9 element, from 0-9, named isChecked
Initialize it with false.
Whenever you found a matching number, say 9, turn the boolean element to true, so that you don't count it again (isChecked[9] = true).
Here is the code:
var isChecked = [];
function resetArray(input) {
for (var i = 0; i < 10; i++) {
input[i + ''] = false;
}
}
resetArray(isChecked);
var firstNumber = '989',
secondNumber = '999',
correctNumbers = 0,
fNum, sNum;
for (var i = 0; i < firstNumber.length; i++) {
fNum = firstNumber.charAt(i);
// Skip already checked numbers
if (isChecked[fNum]) {
continue;
}
for (var j = 0; j < secondNumber.length; j++) {
sNum = secondNumber.charAt(j);
if (fNum == sNum && !isChecked[sNum]) {
correctNumbers++;
isChecked[sNum] = true;
}
}
}
console.log(correctNumbers);
Tested on JSFiddle.
If you find anything unclear, feel free to ask me :)
(except maybe declaring an array and putting all the repeated numbers in there and only incrementing it if none of the numbers match, but that's super inefficient)
That approach is a good one, and can be made efficient by using a HashSet of Integers. Everytime you encounter a common digit, you do a contains on the set to check for that digit (gets in HashSets are of constant-time complexitiy - O(1), i.e. super quick), and if it's present in there already, you skip it. If not, you add it into the set, and increment your correctNumbers.
I believe this would help
int found=0; for (int i = 0; i < mString.length(); i++) {
for (int j = 0; j < nString.length(); j++) {
if (mString.charAt(i) == nString.charAt(j)) {
if(found==0){
correctNumbers++;
}
}
}
}
You could try making another 1D array of
int size = nstring.length() * mstring.length();
bool[] array = new bool[size];`
and then have that store a boolean flag of whether that cell has been updated before.
you would find the unique index of the cell by using
bool flag = false
flag = array[(i % mString.length()) + j)];
if(flag == true){
<don't increment>
}else{
<increment>
array[(i % mString.length()) + j)] = true;
}
you could also do this using a 2d array that basically would act as a mirror of your existing table:
bool[][] array = new bool[mstring.length()][nString.length()];
Why not just use the new stream api? Then it's just that:
Arrays.stream(mString).flatMapToInt(s -> s.chars()).distinct().count();
I'll explain:
Arrays.stream(mString) -> Create stream of all strings.
flatMapToInt -> create single concatenated stream from many IntStreams
s -> s.chars() -> Used above to create streams of characters (as ints)
distinct -> remove all duplicates, so each character is counted only once
count -> count the (unique) characters

Why is iterative permutation generator slower than recursive?

I'm comparing two functions for use as my permutation generator. This question is about alot of things: the string intern table, the pros and cons of using iteration vs recursion for this problem, etc...
public static List<String> permute1(String input) {
LinkedList<StringBuilder> permutations = new LinkedList<StringBuilder>();
permutations.add(new StringBuilder(""+input.charAt(0)));
for(int i = 1; i < input.length(); i++) {
char c = input.charAt(i);
int size = permutations.size();
for(int k = 0; k < size ; k++) {
StringBuilder permutation = permutations.removeFirst(),
next;
for(int j = 0; j < permutation.length(); j++) {
next = new StringBuilder();
for(int b = 0; b < permutation.length(); next.append(permutation.charAt(b++)));
next.insert(j, c);
permutations.addLast(next);
}
permutation.append(c);
permutations.addLast(permutation);
}
}
List<String> formattedPermutations = new LinkedList<String>();
for(int i = 0; i < permutations.size(); formattedPermutations.add(permutations.get(i++).toString()));
return formattedPermutations;
}
public static List<String> permute2(String str) {
return permute2("", str);
}
private static List<String> permute2(String prefix, String str) {
int n = str.length();
List<String> permutations = new LinkedList<String>();
if (n == 0) permutations.add(prefix);
else
for (int i = 0; i < n; i++)
permutations.addAll(permute2(prefix + str.charAt(i), str.substring(0, i) + str.substring(i+1, n)));
return permutations;
}
I think these two algorithms should be generally equal, however the recursive implementation does well up to n=10, whereas permute1, the interative solution, has an outofmemoryerror at n=8, where n is the input string length. Is the fact that I'm using StringBuilder and then converting to Strings a bad idea? If so, why? I thought whenever you add to a string it creates a new one, which would be bad because then java would intern it, right? So you'd end up with a bunch of intermediate strings that aren't permutations but which are stuck in the intern table.
EDIT:
I replaced StringBuilder with String, which removed the need to use StringBuilder.insert(). However, I do have to use String.substring() to build up the permutation strings, which may not be the best way to do it, but it's empirically better than StringBuilder.insert(). I did not use a char array as Alex Suo suggested because since my method is supposed to return a list of strings, I would have to convert those char arrays into strings which would induce more garbage collection on the char arrays (the reason for the OutOfMemoryError). So with this in place, both the OutOfMemoryError and slowness problems are resolved.
public static List<String> permute3(String input) {
LinkedList<String> permutations = new LinkedList<String>();
permutations.add(""+input.charAt(0));
for(int i = 1; i < input.length(); i++) {
char c = input.charAt(i);
int size = permutations.size();
for(int k = 0; k < size ; k++) {
String permutation = permutations.removeFirst(),
next;
for(int j = 0; j < permutation.length(); j++) {
next = permutation.substring(0, j + 1) + c + permutation.substring(j + 1, permutation.length());
permutations.addLast(next);
}
permutations.addLast(permutation + c);
}
}
return permutations;
}
Firstly, since you got OutOfMemoryError, that hints me you have a lot of GC going on and as we all know, GC is a performance killer. As young-gen GC is stop-the-world, you probably get a lot worse performance by suffering from GCs.
Looking at your code, if you dive into the actual implementation of StringBuilder, you could see that insert() is a very expensive operation involving System.arraycopy() etc and potentially expandCapacity(). Since you don't mention your n for permutation I'd assume the n<10 so you won't have the problem here - you would have memory re-allocation since default buffer of StringBuilder is of size 16 only. StringBuilder is basically an auto char array but it's not magic - whatever you need to do by coding up from scratch, StringBuilder also needs to do it.
Having said the above, if you really want to achieve maximum performance, since the length of your string array is pre-defined, why not just use a char array with length = String.length() ? That's probably the best in term of performance.

Java cannot find symbol for loops, logic problems?

Ok, my program in this specific section takes a line of data from a studentAnswer string array, the value of which would be something like TTFFTFTFTF. I am supposed to take this, and compare it against a key array, which might look like TFFFTFTFTF. A student takes a quiz, and my program calculates the points correct.
My intention is to use a separate points array to find the numeric grade for the student. The index of studentAnswer refers to a specific student. So studentAnswer[i] is TTFFTFTFTF. I use substrings to compare each individual T/F against the correct answer in key[], which would have a single T/F in each index. Then, if they are correct in their answer, I add a 1 to the correlating index in points[] and will later find the sum of points[] to find the numeric grade out of ten.
My problem here is that String origAns, used to define the student's original answer string, is getting a Java Error cannot find Symbol. I have tried placing the instantiation of origAns within each different for loop, but I can't get the program to work. Int i is meant to follow each specific student- I have four parallel arrays that will all log the student's ID number, numeric grade, letter grade, and original answers. So that is the intention of i, to go through each student. Then j should be used to go through each of these original student answer strings and compare it to the correct answer...
Logically, it makes sense to me where I would put it, but java doesn't agree. Please help me to understand this error!
for (int i = 0; i < studentAnswer.length; i++){
String origAns = studentAnswer[i];
for (int j = 0; j < key.length; j++){
if (origAns.substring[j] == key[j]){
//substring of index checked against same index of key
points[j] = 1;
}
if (origAns.substring[j] != key[j]){
points[j] = 0;
}
}
}
It sounds like you're trying to call the substring method - but you're trying to access it as if it were a field. So first change would be:
if (origAns.substring(j) == key[j])
Except that will be comparing string references instead of contents, so you might want:
if (origAns.substring(j).equals(key[j]))
Actually, I suspect you want charAt to get a single character - substring will return you a string with everything after the specified index:
if (origAns.charAt(j) == key[j])
... where key would be a char[] here.
You can also avoid doing the "opposite" comparison by using an else clause instead.
You should also indent your code more carefully, for readability. For example:
for (int i = 0; i < studentAnswer.length; i++) {
String origAns = studentAnswer[i];
for (int j = 0; j < key.length; j++) {
if (origAns.charAt(j) == key[j]) {
points[j] = 1;
} else {
points[j] = 0;
}
}
}
And now, you can change that to use a conditional expression instead of an if/else:
for (int i = 0; i < studentAnswer.length; i++) {
String origAns = studentAnswer[i];
for (int j = 0; j < key.length; j++) {
points[j] = origAns.charAt(j) == key[j] ? 1 : 0;
}
}
When you call a method in Java, you use parentheses () instead of brackets [].
Since substring is a method, you should call it like so
if (origAns.substring(j) == key[j])
A few other notes, you should use the equals method for comparisons (especially those comparisons involving Strings.)
if (origAns.substring(j).equals(key[j]))
Also, you should use charAt to extract a single character at some position in a string. substring(j) will return a string of characters starting at position j.
if (origAns.charAt(j).equals(key[j]))
Your explanation is very long and I have not read it from the beginning to end. But I can see at least one problem in your code:
if (origAns.substring[j] == key[j])
You are comparing strings using == instead of using method equals():
if (origAns.substring[j].equals(key[j]))
Substring is a function, not a member, of String objects. Check out the example at the top of this page:
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html
Notice the use of parenthesis instead of brackets.
If you are using a String use charAt function
String studentAnswer = "TTFFTFTFTF";
for (int i = 0; i < studentAnswer.length(); i++)
{
char origAns = studentAnswer.charAt(i);
}
Else if you are using an char array then
char studentAnswer[] = "TTFFTFTFTF".toCharArray();
for (int i = 0; i < studentAnswer.length; i++){
char origAns = studentAnswer[i];
}

Categories

Resources