How to tackle the Codingbat String-2 oneTwo challenge? - java

Here is the problem statement:
Given a string, compute a new string by moving the first char to come after the next two chars, so "abc" yields "bca". Repeat this process for each subsequent group of 3 chars, so "abcdef" yields "bcaefd". Ignore any group of fewer than 3 chars at the end.
Here is my code:
// oneTwo("abc") → "bca"
// oneTwo("tca") → "cat"
// oneTwo("tcagdo") → "catdog"
public String oneTwo(String str) {
String x = "";
if (str.length() < 3) {
return "";
// return empty
} else if (str.length() == 3) {
String s = str.substring(1, str.length());
x = s + str.substring(0, 1); // last two + first char
} else if (str.length() > 3) {
int third = 2;
// start with the third element index of 2
for (int i = 0; i < str.length(); i++) {
if (i == third) {
// given three chars substring first char
// substring last two chars and add that to x
x += (str.substring(third - 1, third + 1) +
str.substring(third - 2, third - 2 + 1));
third += 3;
//work with this line but why??????
}
//third +=3;
// doesn't work with this line but why???????
}// end of for loop
}
return x;
// return modified string x
}
With third +=3 inside of if statement work but when I put that outside of if statement I don't get the desired output. I don't really understand why?

Hope this helps:
public String oneTwo(String str) {
String str2 = "";
for(int i=0; i<str.length()-2; i+=3) {
str2 = str2+str.substring(i+1,i+3)+str.charAt(i);
}
return str2;
}

Because putting it outside the loop will cause third to be increased far too often. After the first iteration i is 0, third is 5, next iteration yields i=1, third=8; i=2, third=11; i=3, third=14, etc. -> i will never reach third.
I would improve your code by dropping the entire if-statement, remove third all together and simply increment by 3 in the for-loop:
for( int i = 2; i < str.length(); i+=3){
x += (str.substring(third-1, third+1) +
str.substring(third-2, third-2 + 1));
}
If I am not misinterpreting your code you are missing logic for leaving the last characters alone if they are not part of group of three characters.

If you face such effects take a piece of paper and write down the values of the variables after each line of your code.
The if block creates an alternative execution path if the condition is true which is in every third loop iteration.
Anything behind the if block is executed in every loop iteration.
So when the line in question is inside the if block (before the closing brace) the value in variable third is only changed every third loop iteration.
When you move the line behind the closing brace the assignment is outside the if block and therefore executed every loop iteration.

For the comment = //work with this line but why??????
The value of "third" variable gets changed in the for loop only with i is equal to third character, otherwise the value of third will keep on increasing eg.
when i = 0, third = 2
when i = 1, third = 5
when i = 2, third = 8
so the if statement never gets triggered and hence it doesn't work. Hope this makes sense.
PS - I highly recommend using IDE debugger to understand this properly.
PS - It's better to use charAt method as compared for substring method for performance reason

public String oneTwo(String str) {
String temp = "";
String result = "";
int i = 0;
while (str.substring(i).length() >= 3) {
temp = str.substring(i, i + 3);
result += temp.substring(1) + temp.charAt(0);
i += 3;
}
return result;
}

public String oneTwo(String str) {
String str1 = "";
if(str.length()<3){
return str1;
}else if(str.length()>=3){
for(int i =0; i<str.length()-2; i=i+3){
str1 = str1 + str.substring(i+1,i+3)+ str.substring(i,i+1);
}
}
return str1;
}

public String oneTwo(String str) {
if(str.length()<3)return "";
return str.substring(1,3)+str.substring(0,1)+oneTwo(str.substring(3));
}
this is fairly simple as a recursive problem

public String oneTwo(String str) {
String newThreeChars = "";
if(str.length()<3){
return newThreeChars;
}
for(int i=0; i<str.length()/3; i+=3){
String threeChars = str.substring(i,i+3);
String redesigned = threeChars.substring(1) + threeChars.charAt(0);
newThreeChars +=redesigned;
}
return newThreeChars;
}

Another solution to look at...
public String oneTwo(String str) {
int i = 0;
String result = "";
Character tmpChar = '\0';
while(i <= str.length()-3){
tmpChar = str.charAt(i);
result = result + str.charAt(i+1) + str.charAt(i+2) + tmpChar;
tmpChar = '\0';
i = i + 3;
}
return result;
}

First, We loop through each letter of the given String just stopping shy of the last two letters because the word we are looking for is three letters long. Then, we are returning true if there is two letter "b"'s exactly one character apart.
public boolean bobThere(String str) {
for (int i = 0; i < str.length() - 2; i++) {
if (str.charAt(i) == 'b' && str.charAt(i+2) == 'b')
return true;
}
return false;
}

For string concatenation in a loop use StringBuilder:
public String oneTwo(String str) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length() - 2; i += 3) {
sb.append(str.charAt(i + 1)).append(str.charAt(i + 2)).append(str.charAt(i));
}
return sb.toString();
}

Related

LeetCode 14. longest common prefix

Question:
Write a function to find the longest common prefix string among an array of strings. If there is no common prefix, return an empty string "".
Example 1:
Input: ["flower","flow","flight"]
Output: "fl"
Example 2:
Input: ["dog","racecar","car"]
Output: ""
Explanation: There is no common prefix among the input strings.
Code:
public class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs==null || strs.length==0)
return "";
for(int i=0;i<strs[0].length();i++) {
char x = strs[0].charAt(i);
for(int j=0;j<strs.length;j++) {
if((strs[j].length()==i)||(strs[j].charAt(i)!=x)) {
return strs[0].substring(0,i);
}
}
}
return strs[0];
}
}
This is the second solution, but I don't understand the inner loop.
I think if the second element in strs returns a string and ends the for loop, the third element will not have a chance to be compared.
You have to check same position in all of the words and just compare it.
positions
word 0 1 2 3 4 5
=====================
w[0] F L O W E R
w[1] F L O W
w[2] F L I G H T
In Java:
class Main {
public static void main(String[] args) {
String[] words = {"dog","racecar","car"};
String prefix = commonPrefix(words);
System.out.println(prefix);
// return empty string
String[] words2 = {"dog","racecar","car"};
String prefix2 = commonPrefix(words2);
System.out.println(prefix2);
// Return "fl" (2 letters)
}
private static String commonPrefix(String[] words) {
// Common letter counter
int counter = 0;
external:
for (int i = 0; i < words[0].length(); i++) {
// Get letter from first word
char letter = words[0].charAt(i);
// Check rest of the words on that same positions
for (int j = 1; j < words.length; j++) {
// Break when word is shorter or letter is different
if (words[j].length() <= i || letter != words[j].charAt(i)) {
break external;
}
}
// Increase counter, because all of words
// has the same letter (e.g. "E") on the same position (e.g. position "5")
counter++;
}
// Return proper substring
return words[0].substring(0, counter);
}
}
Your first loop is itterating over all chars in the first string of array. Second loop is checking char at i posistion of all strings of array. If characters do not match, or length of string is the same as i it returns substring result.
I think the best way to understand is debug this example.
If the char in the second string is different than the char in the first one, then it is correct to return, since it means that the common prefix ends there. Checking the third and following strings is not necessary.
Basically it returns as soon as it finds a mismatch char.
If we first sort them then it would be very easy we have to only go and compare the first and the last element in the vector present there so,
the code would be like,This is C++ code for the implementation.
class Solution {
public:
string longestCommonPrefix(vector<string>& str) {
int n = str.size();
if(n==0) return "";
string ans = "";
sort(begin(str), end(str));
string a = str[0];
string b = str[n-1];
for(int i=0; i<a.size(); i++){
if(a[i]==b[i]){
ans = ans + a[i];
}
else{
break;
}
}
return ans;
}
};
public class Solution {
public string LongestCommonPrefix(string[] strs) {
if(strs.Length == 0)
{
return string.Empty;
}
var prefix = strs[0];
for(int i=1; i<strs.Length; i++) //always start from 1.index
{
while(!strs[i].StartsWith(prefix))
{
prefix = prefix.Substring(0, prefix.Length-1);
}
}
return prefix;
}
}

Repeatedly removing a substring from a string

Problem: Remove the substring t from a string s, repeatedly and print the number of steps involved to do the same.
Example: t = ab, s = aabb. In the first step, we check if t is contained within s. Here, t is contained in the middle i.e. a(ab)b. So, we will remove it and the resultant will be ab and increment the count value by 1. We again check if t is contained within s. Now, t is equal to s i.e. (ab). So, we remove that from s and increment the count. So, since t is no more contained in s, we stop and print the count value, which is 2 in this case.
I tried to solve this using recursion
static int maxMoves(String s, String t) {
if ( null == s || "" == s || null == t || "" == t){
return 0;
}
int i = s.indexOf(t);
if(i != -1) {
return maxMoves(s.substring(0, i)+ s.substring(i+t.length(), s.length()), t) + 1;
} else {
return 0;
}
}
But I am only passing 9/14 test cases. I also tried this,
static int maxMoves(String s, String t) {
int count = 0,i;
while(true)
{
if(s.contains(t))
{
i = s.indexOf(t);
s = s.substring(0,i) + s.substring(i + t.length());
}
else break;
++count;
}
return count;
}
But that also only passed 9/14 cases.
Could anyone help me figure out which cases I am not covering?
Simply you can use String::replaceFirst with a while loop for example:
String s = "aabb";
String t = "ab";
int count = 0;
while (s.contains(t)) {
s = s.replaceFirst(Pattern.quote(t), "");
count++;
}
System.out.println(count);
Use String#replace
String s = "aabb";
String oldstr = s;
String x = "ab";
while(s.contains(x)){
s = s.replace(x, "");
}
System.out.println((oldstr.length()-s.length())/x.length());
An easy and efficient way is to accumulate the string character-by-character in a StringBuilder; if at any time its buffer ends with the string you want to replace, remove it:
StringBuilder sb = new StringBuilder();
int c = 0;
for (int i = 0; i < s.length(); ++i) {
sb.append(s.charAt(i));
int last = sb.length()-t.length();
if (last >= 0 && sb.indexOf(t, last) == last) {
sb.setLength(last);
++c;
}
}
// c is now the number of times you removed t from s.

Check if a String has vowels, and create a new String that doubles the consonants

So here's what I'm trying to do. I take a given string, and make a new string. The new string will be the same as the original string, but will have the consonants doubled.
For example, rabbit becomes rrabbitt and so forth. It only doubles the consonants that aren't already doubled.
Here's what I have so far:
// Returns a new string in which all consonants in the given string are doubled.
// Consonants that are already doubled are not doubled again.
// For example, doubleConsonants("rabbit") returns "rrabbitt".
// It is assumed that in the given string is alphabetic and that no character
// appears more than twice in a row.
// Parameters:
// s - given string
// Returns new string with all consonants doubled
----------------------------------------------------------------------------
public static String doubleConsonants(String s) {
String newString = "";
String vowels = "aeiouAEIOU";
for (int i = 0; i < s.length(); i++) {
boolean hasVowel = false;
for (int n = 0; n == 10; n++){
if ( vowels.charAt(n) == s.charAt(i)) {
newString += s.charAt(i);
i++;
hasVowel = true;
break;
}
}
if (hasVowel = false && s.charAt(i) != s.charAt(i+1) && s.charAt(i) != s.charAt(i-1)) {
newString += s.charAt(i);
i++;
}
else if (hasVowel = false) {
newString += s.charAt(i);
i++;
}
}
return newString;
}
Apparently there are some issues with "dead code" and the boolean hasVowels is "not used". What am I screwing up?
You can do one thing. Using a contains() method will greatly reduce all your work.
for (int i = 0; i < s.length(); i++) { // traverse through the string
if (i < s.length() - 1 && s.charAt(i) == s.charAt(i + 1)) {
newString += s.charAt(i); // handles the double constant special condition like bb in rabbit
i++;
} else if (vowels.contains("" + s.charAt(i))) { //check if the letter is a vowel
newString += s.charAt(i); // if yes, add it once
} else {
newString += "" + s.charAt(i) +s.charAt(i); // else add it twice
}
}
At the end of this code block, you will have the required string stored in newString. you can read more about contains()
Try this.
public static String doubleConsonants(String s) {
return s.replaceAll("(?i)(([^aeiou])\\2+)|([^aeiou])", "$1$3$3");
}
First thing I notice is that the if-statements towards the bottom are using the assignment operator. You want to use the double-equals to test the value. I'll have to look more closely at the logic for more.

Java string index out of bounds in for loop (codingbat function mirrorEnds)

I have a question regarding the problem at codingbat in String 3. Question is as follows:
Given a string, look for a mirror image (backwards) string at both the
beginning and end of the given string. In other words, zero or more
characters at the very begining of the given string, and at the very
end of the string in reverse order (possibly overlapping).
For example, the string "abXYZba" has the mirror end "ab"
mirrorEnds("abXYZba") → "ab"
mirrorEnds("abca") → "a"
mirrorEnds("aba") → "aba"
My code is as follows:
public String mirrorEnds(String string) {
if(string.length() <=1) return string;
String x = "";
int y = string.length() - 1;
for(int i = 0; i < string.length()/2; i++)
{
if(string.charAt(i) == string.charAt(y))
{
x+= Character.toString(x.charAt(i));
y--;
}
else
{
return x;
}
}
return string;
}
When I try it for the following:
"xxYxx"
String length is 5 so index from 0-4. If I run it on my code, the logic will be:
i = 0 and y = 4;
string.charAt(i) == string.charAt(y) //true and i++ and y--
string.charAt(i) == string.charAt(y) //true and i++ and y--
//i is == string.length()/2 at this point
But the problem throws me an error saying indexoutofbounds. Why is this the case?
You are accessing the ith character of the wrong string here:
x += Character.toString(x.charAt(i));
The String x is empty at first, so the character at index 0 doesn't exist.
Access the original string instead.
x += Character.toString(string.charAt(i));
Here my code for this problem , simple one
public String mirrorEnds(String string) {
int start = 0;
int end = string.length()-1;
for(int i=0;i<string.length();i++){
if(string.charAt(start) == string.charAt(end) ){
start++;
end--;
}
if(start != ((string.length()-1)-end)){
break;
}
}
return string.substring(0,start);
}
public String mirrorEnds(String string) {
String g="";
for(int i=0;i<string.length();i++){
if(string.charAt(i)==string.charAt(string.length()-1-i)){
g=g+string.charAt(i);
} else{
break;
}
}
return g;
}
You have a good start, but I think you should consider an even simpler approach. You only need to use one index (not both i and y) to keep track of where you are in the string because the question states that overlapping is possible. Therefore, you do not need to run your for loop until string.length() / 2, you can have it run for the entire length of the string.
Additionally, you should consider using a while loop because you have a clear exit condition within the problem: once the string at the beginning stops being equal to the string at the end, break the loop and return the length of the string. A while loop would also use less variables and would reduce the amount of conditional operators in your code.
Here's my answer to this problem.
public String mirrorEnds(String string) {
String mirror = "";
int i = 0;
while (i < string.length() && string.charAt(i) == string.charAt(string.length() - i - 1) {
mirror += string.charAt(i);
i++;
}
return mirror;
}
Another handy tip to note is that characters can be appended to strings in Java without casting. In your first if statement within your for loop, you don't need to cast x.charAt(i) to a string using Character.toString(), you can simply append x.charAt(i) to the end of the string by writing x += x.charAt(i).
public String mirrorEnds(String str) {
StringBuilder newStr = new StringBuilder();
String result = "";
for (int i=0; i <= str.length(); i++){
newStr.append(str.substring(0, i));
if (str.startsWith(newStr.toString()) && str.endsWith(newStr.reverse().toString()))
result = str.substring(0, i);
newStr.setLength(0);
}
return result;
}
public String mirrorEnds(String string) {
// reverse given string
String reversed = "";
for (int i = string.length() - 1; i >= 0; i--) {
reversed += string.charAt(i);
}
// loop through each string simultaneously. if substring of 'string' is equal to that of 'reversed',
// assign the substring to variable 'text'
String text = "";
for (int i = 0; i <= string.length(); i++) {
if (string.startsWith(string.substring(0, i)) ==
string.startsWith(reversed.substring(0, i))) {
text = string.substring(0, i);
}
}
return text;
}
public String mirrorEnds(String string) {
String out = "";
int len = string.length();
for(int i=0,j = len-1;i<len;i++,j--)
{
if(string.charAt(i) == string.charAt(j))
out += string.charAt(i);
else
break;
}
return out;
}

Finding characters in a string

i'm doing an encoding program where i'm supposed to delete every character in the string which appears twice. i've tried to traverse through the string but it hasn't worked. does anyone know how to do this? Thanks.
public static String encodeScrambledAlphabet(String str)
{
String newword = str;
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
newword += alphabet;
newword = newword.toUpperCase();
for (int i = 0, j = newword.length(); i < newword.length() && j >=0; i++,j--)
{
char one = newword.charAt(i);
char two = newword.charAt(j);
if (one == two)
{
newword = newword.replace(one, ' ');
}
}
newword = newword.replaceAll(" ", "");
return newword;
}
Assuming that you would like to keep only the first occurrence of the character, you can do this:
boolean seen[65536];
StringBuilder res = new StringBuilder();
str = str.toUpperCase();
for (char c : str.toCharArray()) {
if (!seen[c]) res.append(c);
seen[c] = true;
}
return res.toString();
The seen array contains flags, one per character, indicating that we've seen this character already. If your characters are all ASCII, you can shrink the seen array to 128.
Assuming by saying deleting characters that appears twice, you mean AAABB becomes AAA, below code should work for you.
static String removeDuplicate(String s) {
StringBuilder newString = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
String s1 = s.substring(i, i + 1);
// We need deep copy of original String.
String s2 = new String(s);
// Difference in size in two Strings gives you the number of
// occurences of that character.
if(s.length() - s2.replaceAll(s1, "").length() != 2)
newString.append(s1);
}
return newString.toString();
}
Efficiency of this code is arguable :) It might be better approach to count the number of occurences of character by a loop.
So, from the code that you've shown, it looks like you aren't comparing every character in the string. You are comparing the first and last, then the second and next to last. Example:
Here's your string:
THISISTHESTRINGSTRINGABCDEFGHIJKLMNOPQRSTUVWXYZ
First iteration, you will be comparing the T at the beginning, and the Z at the end.
Second iteration, you will be comparing the H and the Y.
Third: I and X
etc.
So the T a the beginning never gets compared to the rest of the characters.
I think a better way to do this would be to to do a double for loop:
int length = newword.length(); // This way the number of iterations doesn't change
for(i = 0; i < length; i++){
for(j = 0; j < length; j++){
if(i!=j){
if(newword.charAt(i) == newword.charAt(j)){
newword.replace(newword.charAt(i), ' ');
}
}
}
}
I'm sure that's not the most efficient algorithm for it, but it should get it done.
EDIT: Added an if statement in the middle, to handle i==j case.
EDIT AGAIN: Here's an almost identical post: function to remove duplicate characters in a string

Categories