Create substring using loop - java

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!

Related

I have to check if an element of the array is the same as the previous one (java)

I followed the advice of the site but I did not find answers that satisfied me.
I have to solve a school exercise. I have an array and I need to check if there is at least a sequence of 3 consecutive "a" characters.
public static String alternative(char[] a) {
String ret = "";
int consecutiveCounter = 0;
int i = 1;
while(consecutiveCounter<3){
while(i<= a.length){
if(a[i] =='a' && a[i] == a[i-1] ) {
consecutiveCounter++;
} else {
consecutiveCounter = 0;
}
i++;
}
}
if (consecutiveCounter == 3) {
ret += "there are three consecutive a char";
} else {
ret += "there are not three consecutive a char";
}
return ret;
}
public static void main(String[] args) {
char[] a = new char[]{'a', 'a', 'b', 'c', 'a', 'b', 'a'};
System.out.println(alternative(a));
}
the terminal gives me this exception:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 7
at Es1.alternative(Es1.java:9)
at Es1.main(Es1.java:31)
I can't increase the value of the index (i) without going out of the array bounds
It may be better to use for loops here, both checking the bounds of array, and the inner counting occurrences of 'a' and returning early as soon as the required limit is reached:
public static String alternative(char[] a) {
for (int i = 0, n = a.length; i < n; ) {
for (int count = 1; i < n && a[i++] == 'a'; count++) {
if (count == 3) {
return "there are three consecutive 'a' chars";
}
}
}
return "there are not three consecutive 'a' chars";
}
It's worth mentioning that String class (which is basically built on char array) has several methods to implement this functionality:
String::contains: "aabbccaaa".contains("aaa") // true
String::indexOf: "aabbccaa".indexOf("aaa") // -1, aaa not found
String::matches (using regular expression): "aabbaaaccaa".matches(".*a{3}.*") // true
Also, I don't think your outer looping will work well.
1.Suppose there is no consecutive char, then consecutiveCounter will remain 0 and while(consecutiveCounter<3) wont end.
2.Even if there is one or two, but it will be set to 0 again, and while(consecutiveCounter<3) wont end.
Here are some suggestions.
Use a for loop from i = 1 to i < a.length. Then i won't exceed the last index of the array.
You are only trying to find 3 consecutive 'a's. So initialize consecutiveCounter to 1.
As soon as the first consecutive pair are found, you increment consecutiveCounter and it will now be 2 which is correct.
Then in the same if clause check to see if that value equals 3. If so, return the String immediately (you may have even 4 or 5 consecutive a's but you also have 3 so return when the count of 3 is first encountered.
Else, if the if statement fails, reset consecutiveCounter to 1 and continue the loop.
At the end, outside the loop, return the string that indicates the requirement wasn't met.
Note: If you were trying to find the maximum number of consecutive a's setting the counter to 1 wouldn't work because you may have no a's at all. But since you are looking for a specific number == 3, it works fine.

How do I reorder characters in a String without the use of Hashmaps?

My code below is giving the following error and I can't figure out why. I am trying to reorder the entered word ("Polish" for example) in the order of:
(First letter, last letter, second letter, second last letter, third letter... so on) so the output should be "Phosli".
Updated code
public static String encodeTheWord(String word1)
{
int b = 0;
int e = word1.length()-1;
String word2 = "";
for (int i=0; i<e; i++)
{
word2 = word2 + word1.charAt(b) + word1.charAt(e);
b+=1;
e-=1;
}
System.out.println(word2);
return (word2);
}
For a word with an even amount of characters (Polish), the order of the characters becomes 051423, so the maximum value of b is 2 and the minimum value is e is from 5 to 3. Thus, your loop should decrement e and increment b twice (so you run the loop for word1.length() / 2 times). Also,
int e = word1.length();
Would need to be:
int e = word1.length() - 1;
For words of an uneven length (word1.length() % 2 > 0) you need an extra check or you will repeat the middle character.
your for loop is wrong, you can get a char at index 0, until the word1.length()-1...
must be
for (int i=0; i<word1.length()-1; i++)
the same applies for this...
word1.charAt(e);
because you defined e as word1.length()

Compressing a file

I want to accomplish a program which can take a textfile and make the size smaller. So far it replaces all the double character occurrences, and now I want to replace "ou" with "1".
I've tried with an if-statement, but it doesn't seem to work quite well.
My method is below:
public String compressIt (String input)
{
int length = input.length(); // length of input
int ix = 0; // actual index in input
char c; // actual read character
int cCounter; // occurrence counter of actual character
String ou = "ou";
StringBuilder output = // the output
new StringBuilder(length);
// loop over every character in input
while(ix < length)
{
// read character at actual index then increments the index
c = input.charAt(ix++);
// we count one occurrence of this character here
cCounter = 1;
// while not reached end of line and next character
// is the same as previously read
while(ix < length && input.charAt(ix) == c)
{
// inc index means skip this character
ix++;
// and inc character occurence counter
cCounter++;
}
if (input.charAt(ix) == 'o' && input.charAt(++ix) == 'u' && ix < length - 1)
{
output.append("1");
}
// if more than one character occurence is counted
if(cCounter > 1)
{
// print the character count
output.append(cCounter);
}
// print the actual character
output.append(c);
}
// return the full compressed output
return output.toString();
}
It's this lines of code I'm referring to.
if (input.charAt(ix) == 'o' && input.charAt(ix + 1) == 'u')
{
output.append("1");
}
What I want to do: Replace characters. I got a text-file which contains "Alice In Wonderland". When my looping through all characters sees an 'o' and an 'u' (like "You"), I want to replace the characters so it looks like: "Y1".
Regards
So most likely you're trying loop from ix = 0 to the length of the string.
First of all my guess is that youre looping up and including string.length(). Which doesnt work, charAt is 0 indexed aka
"abc" has a charAt 0, 1 and 2 but not 3 which gives the error you describe.
Second of all the line you showed uses input.charAt (ix++) which does the following: get the char at position ix (old value) and after that, update the value ix to ix + 1, if you want ix to be updated before the surrounding charAt you'd have to write input.charAt(++ix)
Third of all there is a String.replace function, input.replace("abc", "def") will work great for simple replacements, for more complicated replacements, consider using regex.
This has nothing to do with the charAt method. You need to change your if condition to run it till length-1. It is failing in the last case as it is going out array.
for(int i=0; i<inputString.length() - 1; i++)
{
char temp = inputString.charAt(i);
char blah = inputString.charAt(i+1);
System.out.println("temp: "+ temp);
System.out.println("blah: "+ blah);
}
This works for me!

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);
}

recursive method for flipping card

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.

Categories