Codingbat challenge: mirrorEnds using Stream API - java

Given the task mirrorEnds from CodingBat:
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 beginning 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".
Examples:
mirrorEnds("abXYZba") → "ab"
mirrorEnds("abca") → "a"
mirrorEnds("aba") → "aba"
My solution for this task is the following:
public String mirrorEnds(String str) {
String result = "";
if (str.length() % 2 != 0) {
for (int i = 0; i < str.length() / 2; i++) {
if (str.charAt(i) == str.charAt(str.length() - i - 1)) {
result += "" + str.charAt(i);
} else {
break;
}
}
if (result.length() == str.length() / 2) {
String strEnd = new StringBuilder(result).reverse().toString();
result += "" + str.charAt(str.length() / 2) + strEnd;
}
}
if (str.length() % 2 == 0) {
for (int i = 0; i < str.length() / 2; i++) {
if (str.charAt(i) == str.charAt(str.length() - i - 1)) {
result += "" + str.charAt(i);
} else {
break;
}
}
if (result.length() == str.length() / 2) {
String strEnd = new StringBuilder(result).reverse().toString();
result += strEnd;
}
}
return result;
}
Is it possible to solve this problem using Stream API ?

I think Stream API wouldn't give you any advantage. however, you can optimize your code like this
public String mirrorEnds(String string) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) == string.charAt(string.length() - i - 1)) {
result.append(string.charAt(i));
} else {
break;
}
}
return result.toString();
}

It's doable with IntStream.range() in a single stream-statement.
In order to create a one-liner we will need'll a help of takeWhile(), which was introduced with Java 9. takeWhile() is a so-called short-cercuit operation, i.e. it will break after the first element that doesn't match the given predicate.
public static String mirrorEnds(String str) {
return IntStream.range(0, str.length())
.takeWhile(i -> str.charAt(i) == str.charAt(str.length() - 1 - i))
.map(str::codePointAt)
.collect(StringBuilder::new,
StringBuilder::appendCodePoint,
StringBuilder::append)
.toString();
}
Since CodingBat is still on Java 8, its compiler will complain at the code above.
A very nice, a simple Java 8 compliant solution proposed by #Holger.
It looks for the index of the first mismatch between characters at the beginning and at the end, and generates a substring starting from the very beginning 0 up to themismatch index.
In case if findFirst() returns an empty optional, i.e. the given string is a palindrome and there's no mismatch, the length of the given string will be provided via orElse().
The code below passes all tests on CodingBat.
public String mirrorEnds(String str) {
int mismatch = IntStream.range(0, str.length() / 2)
.filter(i -> str.charAt(i) != str.charAt(str.length() - 1 - i))
.findFirst()
.orElse(str.length());
return str.substring(0, mismatch);
}
Note since CodingBat disallows imports, in order to run the code above on this site you need to use a so-called fully-qualified name of the class java.util.stream.IntStream.

Since you're purposely asking for a stream-solution to this problem, this could be to look for the greatest index i where the first i characters match the last i characters reversed, with i starting from length() / 2 and proceeding by decrementing i until it reaches 0.
The previous algorithm makes sense to be applied only if the given string isn't a palindrome. In fact, in that case, the string itself could be returned immediately.
public static String mirrorEnds(String str) {
if (str.equals(new StringBuilder(str).reverse().toString())) {
return str;
}
OptionalInt maxLen = IntStream.iterate(str.length() / 2, i -> i >= 0, i -> i - 1)
.filter(i -> str.substring(0, i).equals((new StringBuilder(str.substring(str.length() - i))).reverse().toString()))
.max();
return maxLen.isPresent() ? str.substring(0, maxLen.getAsInt()) : null;
}
Output
ab
a
aba
Here is a link to test the code:
https://www.jdoodle.com/iembed/v0/rUL

Related

How does this particular recursion works

How does this particular recursion works
return (X.charAt(m-1) == Y.charAt(n-1) ? count(X, Y, m-1, n-1) : 0) + count(X, Y, m-1, n) 
in the below code?
The below code will return the count of number of times the pattern appears in a given string as a subsequence.
class Subsequence {
public static int count(String X, String Y, int m, int n) {
if (m == 1 && n == 1)
return X.charAt(0) == Y.charAt(0) ? 1 : 0;
if (m == 0)
return 0;
if (n == 0)
return 1;
if (n > m)
return 0;
return (X.charAt(m - 1) == Y.charAt(n - 1) ? count(X, Y, m - 1, n - 1) : 0) + count(X, Y, m - 1, n);
}
public static void main(String[] args) {
String X = "subsequence";//input String
String Y = "sue";// pattern
System.out.print(count(X, Y, X.length(), Y.length()));
}
}
It works as below:
if(X.charAt(m-1) == Y.charAt(n-1))
return count(X, Y, m - 1, n - 1);
else
return 0; //since no match found here
Now, the above code is breakage of the first line of your return statement that uses ternary operator. Don't think that this is the complete break down of this code.
So, once you got this statement, the next step would be call this function again to find any matching characters starting from m- in first string and from n in the pattern string since we need to still match the whole pattern right from the start. However, if we find a match, we are just proceeding to match remaining available characters in both pattern and the string.
But to be honest, there are many better ways to code the pattern matching algorithm as compared to this approach.
You will often find that renaming your variables properly and adding some clarification comments works wonders.
public static int count(String string, String pattern, int stringLength, int patternLength) {
if (stringLength == 1 && patternLength == 1) {
// Two length=1 strings -> if they are equal then they match - obviously.
return (string.charAt(0) == pattern.charAt(0)) ? 1 : 0;
}
if (stringLength == 0) {
// No more string to search - no-match.
return 0;
}
if (patternLength == 0) {
// No more pattern to search - MATCH!
return 1;
}
if (patternLength > stringLength) {
// Can never find a pattern longer than the string.
return 0;
}
return ((string.charAt(stringLength - 1) == pattern.charAt(patternLength - 1)) ?
// Both end with same character - recurse to keep matching.
count(string, pattern, stringLength - 1, patternLength - 1)
// Not match.
: 0)
// ADD any matches further down the string.
+ count(string, pattern, stringLength - 1, patternLength);
}
Sadly the code is wrong somewhere - searching for sue in subsequence prints 7.
I believe it is called a "Ternary Operator", it is another way to do IF statements in a more compact way (ie if you do a load of them at the same time for readability). It has to have somewhere to give the return value (a variable or return statement for example)
As an example it can look like this
boolean test = (condition) ? true : false;
Which is the same as writing
boolean test;
if (condition) {
test = true;
} else {
test = false;
}
CodeHunter's answer is a good example of how the sample code you gave is turned back into an if statement.
What it is doing is:
(Psuedo)
function recursiveCount(stringStart, stringEnd, startLocation, endLocation) {
/* pre-checks */
// Total will be how many times it loops (1 + 1 + 1 + 1 + 1 basically what it is doing)
// If there is no more occurances of that string it adds 0 instead of 1, and the recursion breaks.
return (total + recursiveCount(stringStart, stringEnd, nextStartLocation, nextEndLocation)
}

Reversing the even characters of a word

I was wondering how one would go about making all of the even characters in a word switch with it's partner on the other side of the word.
For example: abc would look like cba or monkey would be eonkMy.
I am looking for one method that would only move the even characters of a string through the use of recursion or calling the method in the return.
public static String revEven(String str)
{
if(str.length() == 0)
return "";
return str.charAt(str.length() - 1) + revEven(str.substring(0, str.length() -1));
}
The output of the current soulution is:
yeknom
edcba
I need the output to be:
yonkmy
ebcda
convert string into Char[]
String input="monkey";
char[] inparr=input.toCharArray();
int len=input.length;
for(int i=0;i<len/2;i++) //iterate till half length that will work
{
if(i%2==0)
{
//character is even
//swap i and len-i index
int temp=inparr[i];
inparr[i]=inparr[len-i];
inparr[i]=temp;
}
else
{
}
}
I would use a StringBuilder. Only loop for the first half of the input String and increment your loop counter by 2. Something like,
StringBuilder sb = new StringBuilder("monkey");
for (int i = 0; i < sb.length() / 2; i += 2) {
int p = sb.length() - i - 1;
char ch = sb.charAt(p);
sb.setCharAt(p, sb.charAt(i));
sb.setCharAt(i, ch);
}
System.out.println(sb);
Which outputs
yoknem
if you change the declaration of int p to
int p = sb.length() - i - 2;
you get
eonkmy

Given a string, does "xyz" appear in the middle of the string?

Given a string, does "xyz" appear in the middle of the string? To
define middle, we'll say that the number of chars to the left and
right of the "xyz" must differ by at most one. This problem is harder
than it looks.
My solution works without the second last line except for one condition: if str="xyx"
Is it possible to modify the for loop to take this into account...I'm struggling with understanding why it doesn't.
My solution does work I'm just trying to get a better understanding of what I'm doing. I know I could add it into the first if statement but I want to know why it doesn't work without it.
public boolean xyzMiddle(String str) {
for (int i=0;i<str.length()-3;i++) {
if (str.substring(i,i+3).equals("xyz")) {
String front =str.substring(0,i);
String end = str.substring(i+3);
int a =Math.abs(front.length() -end.length());
if (a<=1) return true;
}
}
if (str.equals("xyz")) return true;
return false;
I think i remember this question - it's this question from Codingbat, I believe. Excellent web site, learned a lot from that site back when I started programming. There's absolutely no reason to use a loop, though.
public boolean xyzMiddle(String str) {
boolean result = false;
int i = str.length()/2 -1;
if (str.length() >= 3 && (str.substring(i, i+3).equals("xyz") || (str.length()%2 == 0 && str.substring(i-1, i+2).equals("xyz")) )) {
result = true;
}
return result;
}
So, let's walk through this and why it works. Firstly, str.length() >= 3, because if the string isn't at least as long as "xyz", there's no way it can contain "xyz".
There are two main cases to this problem, we need to think of. The string can have an even or an uneven length. In the uneven case, it's easy:
The Uneven case
AAAxyzAAA // length = 9
012345678 // the indexes
^ // that's the middle, which can be calculated by length/2
// (since this is an integer divison, we disregard whatever comes after the decimal point)
So to get the start of the xyz-substring, we simply subtract one from this number - which is exactly what i is doing:
AAAxyzAAA // length = 9
012345678 // the indexes
i // i = length/2-1 = 3
So if str.substring(i, i+3) is xyz, we can return true!
The Even Case
Now, this can be a bit more tricky, since there is no true "middle" of the string. In fact, two indexes could be called the middle, so we have two sub-cases:
AAAAAAAA // length = 8
01234567 // the indexes
^^ // Which one is the true middle character?
In fact, the middle would be between index 3 and 4. However, we are performing integer divisions, length/2 is always the largest (rightmost) of the two possible "middles". And since we calculate i using the middle, the same as in the uneven case applies - str.substring(i, i+3) could be considered the middle part of the string.
AAAxyzAA
01234567
^^^ // str.substring(i, i+3)
i
But suppose our string was AAxyzAAA - that could also be considered the middle part of the string. So we need to move our substring check "to the left" - so we subtract 1 from it.
AAxyzAAA
01234567
^^^ // str.substring(i-1, i+2)
i // i is still at "the old" location
So is it even or not?
To check whether the string is even or uneven, we use the modulo operator, %. The easiest way to think of what it does is "what would be left over after i divided with this number?". So 3 % 2 would be 1. In our case, we want to make sure that the number is divisible by 2 with nothing left over - because that means it was an even number. Therefore, we need to check whether str.length() % 2 == 0 before we make our "move-to-the-left" check. If not, we could risk going out of bounds on the string. If the string was 3 characters long, and we moved one to the left... we would check the substring starting at index -1, and that doesn't make a lot of sense.
Put it all together, and there you go!
I'd say something as simple as:
public void test() {
test("Hello", "ll");
test("Hello", "He");
test("Hello", "el");
test("Hello", "lo");
test("Hello", "Hell");
test("Hello", "ello");
test("Hello", "Hello");
test("Hell", "He");
test("Hell", "el");
test("Hell", "ll");
}
private void test(String s, String p) {
System.out.println(p + (inMiddle(s, p) ? " in " : " not in ") + s);
}
// Is the pattern in the middle of the string.
public static boolean inMiddle(String s, String p) {
int d = s.length() - p.length();
return at(s, p, d / 2) || ((d & 1) == 1 && at(s, p, (d / 2) + 1));
}
private static boolean at(String s, String p, int i) {
return i >= 0 && i < s.length() && s.substring(i).startsWith(p);
}
Results look correct to me:
ll in Hello
He not in Hello
el in Hello
lo not in Hello
Hell in Hello
ello in Hello
Hello in Hello
He not in Hell
el in Hell
ll not in Hell
I have confirmed that this matches Tobias' solution exactly when p = "xyz".
public boolean xyzMiddle(String str) {
int len = str.length();
if (len < 3){ return false; }
int even = (len+1)%2;
int mid = len/2;
return str.substring(mid-1-even, mid+2).contains("xyz");
}
The simplest I could come up with:
public boolean xyzMiddle(String str) {
str = " " + str + " ";
int even = (str.length()+1)%2;
int mid = (str.length())/2;
str = str.substring(mid-1-even, mid+2);
return str.contains("xyz");
}
public boolean xyzMiddle(String str) {
int len = str.length();
if(len < 3) {return false;}
if(len==3 && str.equals("xyz")) {return true;}
int index = middleIndex(str);
String left = str.substring(0,index) ;
String right= str.substring(index+3) ;
//Return based on the difference by at most 1
return (Math.abs(left.length()-right.length()) <=1);
}
public int middleIndex(String str) {
int middleLen = (str.length())/2;
int index= 0;
//Find an index that could be in the middle of the string with
// "xyz"
for(int i=middleLen-2; i < middleLen; i++ ) {
if(str.substring(i, i+3).equals("xyz") ) {
index= i;
}
}
return index;
}
public boolean xyzMiddle(String str) {
if (str.length() >= 3)
{
// if odd
if (str.length() % 2 == 1)
{
//axyzb
//01234
//length = 5; 5 is odd.
//length / 2 = 2;
//2 minus 1 = 1
//1 is where xyz starts
//aaxyzbb
//0123456
//length = 7; 7 is odd.
//length / 2 = 3;
//3 minus 1 = 2
//2 is where xyz starts.
//....
//This pattern works with all odd numbers.
if (str.substring((str.length() / 2) - 1, ((str.length() / 2) - 1) + 3).equals("xyz"))
{
return true;
}
else
{
return false;
}
}
//if even
else
{
//for evens that occur with a larger amount before "xyz" than after
//axyz
//0123
//length = 4; 4 is even;
//4 - 1 = 3;
//3 / 2 = 1
//1 is where xyz starts.
//aaxyzb
//012345
//length = 6; 6 is even;
//6 - 1 = 5;
//5 / 2 = 2
//2 is where xyz starts.
//...
//This pattern works for all even numbers where there is a larger amount of characters before the xyz.
if (str.substring((str.length() - 1) / 2, ((str.length() - 1) / 2) + 3).equals("xyz"))
{
return true;
}
//For the cases where there are more characters after "xyz" than before.
//xyzb
//0123
//length = 4; 4 is even;
//4 - 1 = 3;
//3 / 2 = 1
//1 - 1 = 0;
//xyz starts at 0;
//axyzbb
//012345
//length = 6; 6 is even;
//6 - 1 = 5;
//5 / 2 = 3;
//2 - 1 = 1;
//xyz starts at 1;
//...
//The pattern continues onwards for all cases where there are more characters after xyz than before.
else if (str.substring((((str.length() - 1) / 2) - 1), (((str.length() - 1) / 2) -1) + 3).equals("xyz"))
{
return true;
}
//If there is no instance of xyz in these areas.
else
{
return false;
}
}
}
// If our string is less than 3 in length.
else
{
return false;
}
}
public boolean xyzMiddle(String str) {
return str.length()>2 && str.length()%2==1 && str.substring((str.length()-1)/2-1,(str.length()/2+1)+1).contains("xyz") || str.length()>2 && str.length()%2==0 && (str.substring(str.length()/2-2,str.length()/2+1).contains("xyz") || str.substring(str.length()/2-1,str.length()/2+2).contains("xyz"));
}
public boolean xyzMiddle(String str) {
int index = str.indexOf("x");
if(index < 0)
return false;
while(index >=0 && (index+3 <= str.length()) ){
if(str.substring(index,index+3).equals("xyz") && Math.abs(str.substring(0,index).length() - str.substring(index+3).length()) <=1)
return true;
index = str.indexOf("x",index+1);
}
return false;
}

count uppercase chars in string recursively

I have to solve an exercise, counting all the uppercase chars in a String - recursively - Anyhow I thought I might have found a solution - but it won't work…
Probably you might help me? Thanks!
public static int CountCapitals(String s) {
int counter = 0;
// if (Character.isUpperCase(s.charAt(0)))counter+=1;
if (s.length() == 0)
return counter;
if (s.length() == 1 && s.charAt(0) < 65 && s.charAt(0) > 90)
return 0;
if (s.charAt(0) < 'A' && s.charAt(0) > 'Z') {
return CountCapitals(s.substring(1));
}
if (s.charAt(0) >= 'A' && s.charAt(0) <= 'Z')
counter++;
return CountCapitals(s.substring(1));
}
The problem with your code is the use of counter: each level of invocation has its own counter, initially set to zero. The ++ operator at the bottom has no effect.
You need to compute the result of this invocation based on the result of the previous invocation. Your base case (i.e. s.length() == 0) is fine; the rest of your code needs to change so that it returns whatever CountCapitals(s.substring(1)) when the first letter is non-capital; when the first letter is capital, your function should return 1 + CountCapitals(s.substring(1)).
You need to consider the case when the length of string is 1 and the only character is uppercase (in this case, you should return 1).
Also you need to pass in the counter as a parameter rather than expecting it to "carry over" into other function calls.
This recursion should do just what you want:
public static int countCapitals(String s) {
if (s.length() == 0) return 0;
int cap = Character.isUpperCase(s.charAt(0)) ? 1 : 0;
return countCapitals(s.substring(1)) + cap;
}
If this wasn't a home assignment, you could try an iterative approach which is about 5-10 times faster:
public static int countCapitals(String s) {
int count = 0;
for (int idx = 0; idx < s.length(); idx++) {
if (Character.isUpperCase(s.charAt(idx))) {
count++;
}
}
return count;
}
You don't really need to use a counter variable to keep track of the number of capitals. Instead, you can just the recursive calls, themselves, to keep track of the total:
public static int CountCapitals(String s)
{
if (s.length() == 1)
return (Character.isUpperCase(s.charAt(0)) ? 1 : 0);
else
return CountCapitals(s.substring(1)) +
(Character.isUpperCase(s.charAt(0)) ? 1 : 0);
}
If this is for an assignment and you have to use ASCII values, then fine, but if not, you really should just Character.isUpperCase(char c). In case you're not familiar with the conditional operator, it's defined as follows:
if(someExpression == true)
{
//output 1
}
else
{
//output 0
}
is represented succinctly as:
(someExpression == true) ? 1 : 0
NB:
In your example, counter is set to 0 at the beginning of each method call, so that's why it's not working. If you really want to use a counter, pass it as a parameter to the method instead, and update the parameter with each method call. When you get to the end of the String, simply return the parameter.
You try this
public class HelloWorld{
public static int isUpperCase(String str){
if(str.length()==0) return 0;
boolean check =Character.isUpperCase(str.charAt(0));
if(check){
return isUpperCase(str.substring(1))+1;
}
return isUpperCase(str.substring(1));
}
public static void main(String []args){
String n= "FSAsdsadASdcCa";
System.out.println(isUpperCase("FSAsdsadASdcCa"));
}
}

some weird stff im running into on java

So im working on java codingbat and this is the question:
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 passed all the test except for the other test, which is not specified. I dont know what's wrong with it.
public String mirrorEnds(String string) {
String input = string, mirror = "";
int length = string.length();
for (int n = 0; n < (length+1) / 2; n++) {
if (input.charAt(n) != input.charAt(length - n - 1)) {
break;
}else if(length%2 == 1 && n == (length - 1)/2){
// System.out.println("length/2 = " );
return input;
}
else {
mirror += input.charAt(n);
}
}
return mirror;
}
You were correct in not needing to go though the entire word, but your logic is more complex than it needs to be, making it harder to find and fix the problem. The root cause of the test failure is in the last return statement. It must return string if the loop completes without breaking. You can fix your code by changing break; to return mirror; and changing the last return mirror; to return input;
The test that is failing is one like this:
mirrorEnds("abba") -> "abba"
A much simpler version of your code can be created like this:
public String mirrorEnds(String string) {
int len = string.length();
for (int i=0; i < len/2; ++i)
if (string.charAt(i) != string.charAt(len - 1 - i))
return string.substring(0, i);
return string;
}
mirrorEnds("abba")?
Anyways, I'm sure you could come up with a better question name than "some weird stuff"...
Since you are dividing n by 2 in your loop termination condition, it will end when halfway through the word. This is enough to tell the word is a palindrome, but not enough to build your output correctly. You have a condition handling palindrome with odd numbers of letter, but not even numbers of letters. I believe the failing test will be of the form "abba", where I believe what you have will return "ab", instead of "abba".
If you change you loop to:
for (int n = 0; n < length; n++) {
I believe it should be doing what you want. This also makes the short circuit case unnecessary, so:
for (int n = 0; n < length; n++) {
if (input.charAt(n) != input.charAt(length - n - 1)) {
break;
}
else {
mirror += input.charAt(n);
}
}
The first test I tried was with the string "abba" which fails. It returns ab, and not abba. As femtoRgon mentioned, you're not going through the entire word, which may some times be necessary. femtoRgon's solution works, as well as taking a slightly different approach to iterating through the word as follows:
public String mirrorEnds(String string) {
boolean matches = true;
StringBuilder mirrorEnd = new StringBuilder();
int index = 0;
while (matches && index < string.length()) {
if (string.charAt(index) == string.charAt(string.length() - index - 1))
mirrorEnd.append(string.charAt(index));
else
matches = false;
index++;
}
return mirrorEnd.toString();
}
public String mirrorEnds(String string) {
String comp="";
for(int i=0; i<string.length(); i++){
if(string.charAt(i)==string.charAt(string.length()-(i+1)))
comp= comp+ string.charAt(i);
else break;
}
return comp;
}

Categories

Resources