recursive method for flipping card - java

in java - if i have an index card with the letter C written on side, and S on the other. how do i write a recursive method that print each sessoin of dropping the cards with C's and S's. for example: if i drop it 4 times,all possible ways to drop it ara as follows in this specific order:
SCSC
SCSS
SSCC
SSCS
SSSC
SSSS

It's actually rather simple:
public void flip(List<String> items, int length, String prefix)
{
if (prefix.length() == length) {
items.add(prefix);
return;
}
increments(items, length, prefix + 'C');
increments(items, length, prefix + 'S');
}
As you can see, there are two recursive calls, one for the 'C' character and one for the 'S' character, and the recursion base case is when the prefix length is the length specified (4, in your case)
Call like so:
List<String> inc = new LinkedList<>();
increments(inc, 4, "");
for (String s : inc)
System.out.println(s);
Which outputs:
CCCC
CCCS
CCSC
CCSS
CSCC
CSCS
CSSC
CSSS
SCCC
SCCS
SCSC
SCSS
SSCC
SSCS
SSSC
SSSS
This method can easily be generalised for any array of characters:
public void increments(List<String> items, int length,
String prefix, char[] chars)
{
if (prefix.length() == length) {
items.add(prefix);
return;
}
for (char c : chars)
increments(items, length, prefix + c, chars);
}
List<String> inc = new LinkedList<>();
increments(inc, 4, "", new char[] {'C', 'S'});
for (String s : inc)
System.out.println(s);
This yields the same output.
Note: this method has a high complexity, O(pow(chars.length, length)), so attempting to run it with a large input size will take a (very) long time to complete.
Integer.toBinaryString(int) Approach
As requested:
public void increments_ibs(List<String> items, int n, int i)
{
if (i >= Math.pow(2, n))
return;
String bs = Integer.toBinaryString(i);
while (bs.length() < n)
bs = "0" + bs;
items.add(bs.replaceAll("0", "C").replaceAll("1", "S"));
increments_ibs(items, n, i+1);
}
This is essentially an iterative algorithm written recursively.

So this problem is actually a lot simpler if you realize that the card flipping is actually just counting upwards by one in binary.
With that in mind, you can just keep track of an X bit long number (where X is the number of cards you want to keep track of) and then print the cards by checking to see where there are 1's (S) or 0's (C).
After you do that, check to see if all of the positions have 1's. If they do, exit recursion. If they don't, add one to the number and run the function again.
Edit
If you know the number of bits in the number beforehand (something easy enough to calculate) you could use bit shifting (>> will be the best here). So for instance, you could have a quick for loop to go through the number and check each position.
int cardTracker = 0b0110; //this is 6 in decimal
char[] toPrint = new char[4];
//The 4 here is the length of the binary number
for(int i = 0; i < 4; i++)
{
//This if statement checks if the last number is 0 or 1
if((cardTracker >> ((4 - 1) - i) % 2 == 0)
{
toPrint[i] = 'C';
}
else
{
toPrint[i] = 'S';
}
}
The above will print the following if you were to print the contents of toPrint.
CSSC
Hopefully you can use the above and adapt it to your code for the recursive problem.

Related

Compute sets of size n given an array

I'm not sure if my question title makes sense or even if I completely know what I'm trying to get at, but say you are given some letters in an array, and a number n:
char[] arr = ['a','b'], n = 4
How can you compute all the permutations of size n?
aaaa
bbbb
abab
bbaa
aabb
... (and so on)
I can't figure out how to do this and would really appreciate some help. I guess why I'm confused is because the size you want to make (4) is greater than the number of elements in your array and I haven't seen that before.
One solution to your problem could be to create a recursive method that makes all permutations in the set (in this case a and b) and call it n times to form a string.
In pseudo code...
Recursive function that accepts a String parameter:
if the length is 4, add string to results
otherwise, loop over chars:
recursively call function with param + char
Start by calling function with a blank.
Try to see the question in a different way, say you are given n boxes, in every box you need to put a letter selected from array arr, since every letter in the array can be used more than once, so for every box you have as many choices as arr.length, to group all the boxes together, you'll have a total of arr.length ^ n permutations, a simple working example in java using recursion:
public class Test {
static List<String> res;
public static void main(String[] args){
char[] arr = {'a','b'};
int n = 4;
res = new ArrayList<>();
permutate(arr, new char[n], n, 0);
System.out.println("size = " + res.size());
System.out.println("permutations = " + Arrays.toString(res.toArray()));
}
static void permutate(char[] arr, char[] permutation, int n ,int idx){
if(idx == n){
res.add(new String(permutation));
return;
}
for(int i = 0; i < arr.length; i++){
permutation[idx] = arr[i];
permutate(arr, permutation, n, idx + 1);
}
}
}

Create substring using loop

I am given a string from which i have to find out sub-strings that satisfy the following conditions
all characters in the sub-string are same. eg: aa,bbb,cccc.
all the character except the middle character have to be the same.
eg: aba, bbabb, etc.
I've made an algo something like this
I beak the string using two loops 1st loop holds the first char and the second loop traverses through the string.
Then i send the sub-string to the vet() to see if the substring contains less than or equals two character.
If the sub-string contains two character then i check if its a palindrome
public static int reverse(String s)
{
String wrd="";
for(int i = s.length()-1 ;i>=0;i--)
wrd = wrd + s.charAt(i);
if(s.equals(wrd))
return 1;
else
return 0;
}
public static boolean vet(String s)
{
HashSet<Character> hs = new HashSet<>();
for(char c : s.toCharArray())
{
hs.add(c);
}
if(hs.size() <= 2)
return true;
else
return false;
}
static long substrCount(int n, String s) {
List<String> al = new ArrayList<>();
for(int i=0;i<s.length();i++)
{
for(int j=i;j<s.length();j++)
{
if(vet(s.substring(i,j+1)))
{
if(reverse(s.substring(i,j+1)) == 1)
al.add(s.substring(i,j+1));
}
}
}
return al.size();
}
This code works fine for small strings, however if the string is big say ten thousand character, this code will throw Time limit exception.
I suspect the loop that breaks the string and create the sub-string in the substrCount() is causing the time complexity as it has nested loops.
Please review this code and provide a better way to break the string or if the complexity is increasing due to some other section then let me know.
link : https://www.hackerrank.com/challenges/special-palindrome-again/problem?h_l=interview&playlist_slugs%5B%5D=interview-preparation-kit&playlist_slugs%5B%5D=strings
You can collect counts from left side and right side of the string in 2 separate arrays.
Now, we collect counts in the fashion of if previous char equals current char, increase count by 1, else set it to 1.
Example:
a a b a a c a a
1 2 1 1 2 1 1 2 // left to right
2 1 1 2 1 1 2 1 // right to left
For strings that have all characters equal, we just collect all of them while iterating itself.
For strings with all equal except the middle character, you can use above the above table and you can collect string as below:
Pseudocode:
if(str.charAt(i-1) == str.charAt(i+1)){ // you will add checks for boundaries
int min_count = Math.min(left[i-1],right[i+1]);
for(int j=min_count;j>=1;--j){
set.add(str.substring(i-j,i+j+1));
}
}
Update:
Below is my accepted solution.
static long substrCount(int n, String s) {
long cnt = 0;
int[] left = new int[n];
int[] right = new int[n];
int len = s.length();
for(int i=0;i<len;++i){
left[i] = 1;
if(i > 0 && s.charAt(i) == s.charAt(i-1)) left[i] += left[i-1];
}
for(int i=len-1;i>=0;--i){
right[i] = 1;
if(i < len-1 && s.charAt(i) == s.charAt(i+1)) right[i] += right[i+1];
}
for(int i=len-1;i>=0;--i){
if(i == 0 || i == len-1) cnt += right[i];
else{
if(s.charAt(i-1) == s.charAt(i+1) && s.charAt(i-1) != s.charAt(i)){
cnt += Math.min(left[i-1],right[i+1]) + 1;
}else if(s.charAt(i) == s.charAt(i+1)) cnt += right[i];
else cnt++;
}
}
return cnt;
}
Algorithm:
The algorithm is the same as explained above with a few additional stuff.
If the character is at the boundary, say 0 or at len-1, we just look at right[i] to count the strings, because we don't have a left here.
If a character is inside this boundary, we do checks as follows:
If previous character equals next character, we check if previous character does not equal current character. We do this because, we want to avoid future addition of strings at the current iteration itself(say for strings like aaaaa where we are at the middle a).
Second condition says s.charAt(i) == s.charAt(i+1), meaning, we again have strings like aaa and we are at the first a. So we just add right[i] to indicate addition of strings like a,aa,aaa).
Third does cnt++ meaning addition of individual character.
You can make a few optimizations like completely avoiding right array etc, but I leave that to you as an exercise.
Time complexity: O(n), Space complexity: O(n)
Your current solution runtime is O(n^4). You can reduce it to O(n^2logn) by removing the number of character count in substrings and optimising the palindrome check portion.
To do so, you have to pre-calculate an array say "counter" where every position of the "counter" array indicates number of different characters from starting index to that position.
After constructing the array, you can check if a substring has more than two characters in O(1) by subtracting the end position and starting position value of counter array. If the value is 1 then there have only one character in the substring. If the value is 2, then you can binary search in the counter array between the substrings starting and end positions to find the position of single character. After finding out the position of the single character its straight forward to check if the substring is palindrome or not.
UPDATE!
Let me explain with an example:
Suppose the string is "aaabaaa".
So, the counter array would be = [1, 1, 1, 2, 2, 2, 2];
Now, lets assume for a specific time, the outer for loops value i = 1 and the inner for loops value j = 5; so the substring is "aabaa".
Now to find the number of character in the substring by following code:
noOfDifferentCharacter = counter[j] - counter[i-1] + 1
If the noOfDifferentCharacter is 1 then no need to check for palindrome. If the noOfDifferentCharacter is 2 like in our case we need to check if the substring is palindrome. To check if the substring is palindrome have to perform a binary search in the counter array from index i to j to check for the position where the value is greater than its previous index. In our case the position is 3, then you just need to check if the position is the middle position of the substring. Note that the counter array is sorted.
Hope this helps. Let me know if you don't understand any step. Happy coding!

It works if use 'int' but fails when I use 'long' . How do I make this function return long

the function only returns value when I declare 'n' as int, but returns null when i use 'long'.
Given a string and a value n, the string should be concatenated n number of times. in the concatenated string, we will take the first n characters in that string and return the number of letter 'a' that appeared.
Print a single integer denoting the number of letter a's in the first n letters of the infinite string created by repeating s infinitely many times.
In this function, two parameters are passed, a string and a long value. The code works very well if use an int value instead of long. Please how do i fix this long and int issue ?
public class StringLettersRepeat {
static long repeatedString(String s, long n) {
String string = "";
int count =0;
for(int i=0; i<n; i++){
string+=s;
}
char[] strChar = string.toCharArray();
char[] result = new char[(int) n];
for(int i=0; i<strChar.length;i++){
result[i]=strChar[i];
}
for(char str : result){
if('a'==str){
count++;
}
}
return count;
}
public static void main(String[] args) {
long result = repeatedString("a", 1000l);
System.out.println(result);
}
}
I expect the output to return a value, which is the number of count.
for example, if I enter string "aba" and n=7, it should return 5.
But if i pass in a string, say 'a' with n=100000000000, it's supposed to return 100000000000 but it doesn't work. Please what's possibly wrong with my code?
Given your example of calling repeatedString("aba", 7), the resulting string would be "abaabaa", and has 5 a's, as you said.
But, you don't actually have to build that result string. Instead, realize that the result string is the original string repeated 2 times, plus the first 1 characters of the string, both of which can easily be calculated using division and remainder math:
long repeats = n / s.length();
long extra = n % s.length();
Now, if you count the number of a's in the string, you can multiply by repeats. You don't need to repeat the counting operation. If you then also count the number of a's in the first extra characters of string, you have your final result.
int countFull = 0, countExtra = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == 'a') {
countFull++;
if (i < extra)
countExtra++;
}
}
Then calculate the total and return it:
return countFull * repeats + countExtra;
This code also runs a lot faster, because you only have to iterate s once, it doesn't matter for performance what n is, and you don't copy any characters, so it also uses a lot less memory. It actually doesn't use any memory.
Big-O is:
Performance: O(m) where m is length of input string.
Memory: O(1)
Neither is related to n.
Test
System.out.println(repeatedString("aba", 7));
System.out.println(repeatedString("a", 100000000000L));
Output
5
100000000000

Remove the duplicate characters in a string

This is from cracking the Coding Interview Book.
Design an algorithm and write code to remove the duplicate characters in a string
without using any additional buffer. NOTE: One or two additional variables are fine.
An extra copy of the array is not.
In the book it says time complexity is $O(N^2)$. How can we tell that the time complexity is $O(N^2)$ from the solution?
I have questions as to how the solution is removing duplicate characters. I have included them in the inline comments below.
public static void removeDuplicates(char[] str) {
if (str == null) return; // if the array is empty return nothing?
int len = str.length; // get the length of the array
if (len < 2) return; // if the array length is only 1 return nothing?
int tail = 1; // initialise tail variable as 1 !
// Why are we starting at second character here (at i = 1),why not start at i = 0 ?
for (int i = 1; i < len; ++i) {
int j;
for (j = 0; j < tail; ++j) { // so are we checking if j is less then 1 as tail has been initialized to 1
if (str[i] == str[j]) break; // stop, if we find duplicate.
}
if (j == tail) { why are we comparing j to tail(1) here ?
str[tail] = str[i]; // assigning the value
++tail; // incrementing tail
}
}
str[tail] = 0; //setting last element as 0
}
-
I completly rely on #pbabcdefp comment as I'm to lazy to test it, but it seems like your algorithm does not work.
I did not like it anyway, here how I would do it with explanation in comments :
public static void main(String[] args) {
removeDuplicates(new char[]{'a','a','b','b','c','d','c'});
}
public static final void removeDuplicates(char[] str)
{
/*
* If the str is not instantiated, or there is maximum 1 char there is no need to remove duplicates as
* it is just impossible to have duplicate with 1 char.
*/
if (str == null || str.length < 2)
return;
//loop over each char
for(int i = 0; i < str.length; i++)
{
//no need to check with earlier character as they were already checked, so we start at i + 1
for(int j = i + 1; j < str.length; j++)
{
//if they match we clear
if(str[j] == str[i])
str[j] = ' ';
}
}
System.out.println(str);
}
That would output a b cd.
First of all, this is a great book, I wish to recommend to everyone!
Generally if you are allowed to use a lots of memory, you can save time, if you are allowed to use a few variables, then you still can solve this problem by a slower algorithm. And there is the complete brute-force algorithm, when you check every possible solution.
public static void removeDuplicates(char[] str) {
if (str == null) return; // if the array is empty return nothing?
The input is a string pointer, so the string exists somewhere in the memory, the code will may modify it, but it stays at the same place. That's why the return type of the function is void, so it doesn't return anything. When it returns, the string at its original place is without duplication.
int len = str.length; // get the length of the array
if (len < 2) return; // if the array length is only 1 return nothing?
Same as above, no return value. If the string is less then 2 character, then it cannot contain a duplication.
From here the logic is the following:
Take the i-th character. Check if it was existing before this place in the string. If it exists, then the algorithm deletes the i-th character. If it doesn't exists then it stays in the string.
The proof that it's the right algorithm:
None of the characters will stay which existed earlier in the string. If a character would exists later in the string, it would be deleted because of the previous rule.
If this would be the algorithm, it would work fine, but there would be "Empty" characters in the string. The string wouldn't be smaller, even tough it should contain less characters.
That's why the algorithm keeps track on the "tail of the output string". That's why the tail is equals 1 at the beginning, since the 1st character will be definitely part of the result string.
When the current character should be deleted, the output string's tail wont move, no new character added to the result. When the current character should be part of the result, then it gets copied to the tail of the result string.
When the algorithm reaches the end of the input string, it closes the result string.
Complexity:
It means, relative to the size of the input, which is called 'n' how many steps the algorithm has to take. Typically cycles and recursions counts only.
This code has 2 for loop embedded into each other.
The external goes from 1 to n every time.
The internal one goes from 0 to tail where tail goes from 1 to n. So the worst case scenario, the internal one goes by average from 1 to n/2.
This means your complexity is n*(n/2). Since 2 is a constant, your complexity is n*n.
The O time complexity is about worst-case. Ignoring the array you get and the actions you do on it, when you have 2 nested for loops bounded by the length of the string, your complexity couldn't be higher than n^2, and thus it is O(n^2) (it is only an upper bound, if you'd like to show that it's also a lower bound more work should be done).
O(N^2) basically means that as the number of inputs increases, N being the number of inputs, the complexity (number of operations performed) will scale porportional to N^2 + some constant value.
So looking at the code, str.length is N. For each element, you compare it to each other element, N compared N times = N^2.
Now O(N^2) is not meant to be exact. By definition it is concerned with only the non-constant factors that contribute to complexity growth. It will never tell you how quickly a particular algorithm will run, it purely tells you how the time it takes to run will scale with fluctuations in the number of elements being operated on.
Use this for remove every duplicate lowercase
static boolean contains(char c, char[] array) {
for (char x : array) {
if (x == c) {
return true;
}
}
return false;
}
public static void main(String[] args) {
String s = "stackoverflow11221113" ;
String result = "";
for(char ch:s.toCharArray()){
if(!contains(ch,result.toCharArray())){
result +=ch;
}
}
System.out.println(result);
}
Use this for remove every duplicated character both lowercase or uppercase
static boolean contains(char c, char[] array) {
for (char x : array) {
if (x == c) {
return true;
}
}
return false;
}
public static void main(String[] args) {
String s = "StackOverFlow11221113" ;
String result = "";
for(char ch:s.toCharArray()){
if(!contains(ch,result.toLowerCase().toCharArray())){
result +=ch;
}
}
System.out.println(result);
}

How to go about making this type of combination?

I have a four letter string of letters, such as ASDF and I want to find all 3 (three) letter combinations using these letters and the three letter combination does not need to form a real word.Ex.
AAA AAS AAD AAF
ADA ADS ADD ADF
...............
SSA SSD SSF SSS
I am relatively new to Java, having just learned how to use the String class and using loops and conditional statements. The only way that I know how to do this is by a massive and very tedious set of for loops and if statements that account for every possibility that could arise. This would look like:
public static void main(String[] args)
{
String combo = "";
for(int counter = 1; counter <= 16; counter++){
combo = "A";
if(counter == 1){
combo = combo + "AA";
}
// This would continue on for all the possibilities starting with "A" and
// then move on to "S" as the lead character
}
}
I know that this is one of the worst ways to go about this problem, but I am really stuck as to how to do it another way. It would be easier if I had 3 letters and made the 3 letter combos, as then I could just get each letter from the array and just rearrange them, but as I'm only using 3 of the 4 letters it is more difficult. Any advice on how to get this done in an more efficient manner?
Use a recursive function.
Like this (not tested, don't have a Java compiler on my laptop).
Performance could probably be boosted by using StringBuilder.
static void printAllPossibilities(String charSet, int length) {
printAllPossibilities_(charSet, length, "");
}
static void printAllPossibilities_(String charSet, int length, String temp) {
if (length == 0) {
System.out.println(temp);
return;
}
for (int i = 0; i < charSet.length(); i++)
printAllPossibilities_(charSet, length - 1, temp + charSet.charAt(i));
}
Usage:
printAllPossibilities("ASDF", 4); // Print all 4-letter combinations out of "ASDF"
printAllPossibilities("bar", 2); // Print all 2-letter combinations out of "bar"
For the general case (all combinations of N chars out of M), the solution by #Qntm is exactly what you need... but, use a StringBuilder as he said, and just change the last character instead of constructing strings like 'temp + charSet.charAt(i)'.
If you need exactly 3 chars out of N, it's easier to just do 3 nested loops:
for (int char1 = 0; char1 < charSet.length(); char1++) {
for (int char2 = 0; char2 < charSet.length(); char2++) {
for (int char3 = 0; char3 < charSet.length(); char3++) {
System.out.println(""+charSet.charAt(char1)+charSet.charAt(char2)+charSet.charAt(char3));
}
}
}

Categories