After Method: Cut String at '-' or ' ' - java

The input string may contain hyphens and spaces that mark places to truncate the string. The output string will be a truncated version of the input string, and the input int value is the desired length of the output string. The output string should truncate the input string at the first legal spot so the output string will have the desired length. If the truncation happens at a space, the output does not include the space, but if the truncation happens at a hyphen, the output includes the hyphen. No other hyphens are included in the output, but the other spaces are.
How can I fix my code to get the "What code should output" output without using arrays and breaks?
public static String after(int length, String s1) {
StringBuilder sb = new StringBuilder();
int x = 0;
for(; sb.length() < length && x < s1.length() - 1; x = x + 1) {
if(s1.charAt(x) != '-') {
sb.append(s1.charAt(x));
}
}
for(; x < sb.length() && s1.charAt(x) - 1 != '-'; x = x + 1) {
sb.append(s1.charAt(x));
}
if(s1.charAt(x) == ' ' && s1.length() + 1 == s1.length()) {
sb.append(s1.charAt(x));
}
return sb.toString();
}
Input are:
HW2.after(5, "La-te-ly the-re.")
HW2.after(6, "La-te-ly the-re.")
HW2.after(7, "La-te-ly the-re.")
I am getting the output :
"Latel"
"Lately"
"Lately "
What Actually output is:
"Late-"
"Lately"
"Lately the-"

Sounds like a homework question, so I'll just give you the approach I used, so you can think through the coding for yourself...
Stop your first loop one short of the number of requested characters. You have to not skip spaces, but keep a count of them, and subtract that from sb.length() you're testing in your loop because your desired length excludes spaces.
Then deal with the case where you're now pointing to a space, by testing for that, and if true, adding it to the output string and advancing x.
Then keep looking at the next character and adding it to the result as long as it isn't a dash or a space.
Finally, if you stopped on a dash, add it to the output.

Related

How to sum consecutive equal digits in a number in Java

The following question was asked in my last interview (yesterday), and I'm trying to solve it since then (couldn't solve it in the interview).
Sorry for any grammar mistakes or any logical mistakes, I don't have the question, it was written by memory:
You are given a number in a string format, for example: "14438832066".
You got to sum up the consecutive equal digits in that number. If no
consecutive equal digit was found, just add the digit to the result.
for example: solution(19938832066) => 11831632012
Explanation: first digit is 1.
The second and third digits are both 9 which means they will turn into 18 in the result string.
So on
with the rest of the digits (as you can see, the last 2 digits are both 6 which means they will turn into 12 in the result string).
You are required to do that for the result string as well, if needed, until no equal consecutive digits are found in the number.
Example:: number: 14438832066 solution( "19938832066") ->"11831632012" -> "2831632012"
Explanation: first result is 11831632012, but then you can see that there are still equal consecutive digits : the first and the
second digits are both 1. So process that number as well.
You are given a string and must return a string.
My solution:
I couldn't write the solution, I don't know why. It's a pretty simple question, I thought going recursive at first but didn't want to complex things.
I wrote 2 helper methods:
one that returns a boolean whether the number consists of equal consecutive digits.
one that actually makes the business logic:
turn the string into a char array
create a counter that will count instances of the same digit - (int counter = 1).
loop on the array from the first to the one before the last element :
inside the loop:
//equal digit was found - increment counter and continue to next digit
if char[i] == char[i+1] then counter++
//calculation in case we are done counting the same digit
else if counter > 0 then result.append(counter*digit[i])
// if no consecutive equal digit was found
else result.append(digit[i])
end loop: return result
Problems I had:
I created the counter inside the loop, so each iteration it got rested. took me few minutes to realize.
I had troubles realizing that 'int(digit[i])' doesn't give me the numeric value of the char, it gives the ASCII value. I had to use "Character.getNumericValue" (don't remember the exact name of the method).
Because of these problems, it took me 45 minutes to write the solution which in the end didn't even work.
I'll be glad to get a working solution, and even better - to get any feedback and tips on my solution and what, in your opinion, were my mistakes.
Thank you.
Your pseudo-code seems alright, as far as it goes. What's missing is that you don't repeatedly check the result string to see if another pass is required. I also show how you don't need to remember the API to convert a character to a digit; if you know the digits are decimal, you can interpret them yourself. As an interviewer, I would accept that there is an API that you can't precisely remember or your home-brew solution as equally valid.
String transform(String number) {
while (true) {
String result = collapse(number);
if (result.equals(number)) return result;
number = result;
}
}
private static String collapse(String number) {
StringBuilder result = new StringBuilder();
for (idx = 0; idx < number.length(); ) {
int mark = idx;
int digit = digitAt(number, idx++);
while (idx < number.length() && digitAt(number, idx) == digit) ++idx;
result.append((idx - mark) * digit);
}
return result.toString();
}
private static int digitAt(String num, int index) {
char ch = number.charAt(index);
if (ch < '0' || ch > '9') throw new IllegalArgumentException();
return ch - '0';
}
The preceding is a naïve approach that transforms the string until there are no changes. I suspect there might be a more "elegant" approach that works from left to right through the input in a single pass, but it would take some thought, and I probably couldn't come up with that in an interview.
Here's an algorithm that uses recursion and a for-loop to add consecutive equal digits. I think the code is pretty self-explanatory but please ask if you have any queries.
public static String addConsecutiveDigits(String number) {
char[] arr = number.toCharArray();
StringBuilder result = new StringBuilder();
boolean foundConsecutive = false; // boolean flag for checking if the number contained consecutive equal digits
for (int i = 0; i < arr.length; i++) {
int digit = arr[i] - '0'; //Subtracting ascii values to get integer values
int newNumber = digit;
if (i != arr.length - 1) {
int nextDigit = arr[i + 1] - '0';
if (digit == nextDigit) { // check if the digits are consecutive digits
newNumber = digit + nextDigit;
i++; // increment i as we have already added the i+1 digit
foundConsecutive = true;
}
}
result.append(newNumber);
}
if (!foundConsecutive) // if no consecutive equal digits were found then return the result;
return result.toString();
else // recurse to check for more consecutive equal digits
return addConsecutiveDigits(result.toString());
}
I'm not a Java guy, so this code might not be ideal but I would do something like this:
public String solve(String input)
{
String result = "";
int i = 0;
while (i < input.length())
{
var first = input.charAt(i);
if (i == input.length() - 1){
result += first;
break;
}
var second = input.charAt(i + 1);
if (first == second){
result += (Character.getNumericValue(first) + Character.getNumericValue(second));
i += 2;
} else {
result += first;
i += 1;
}
}
return result;
}
For the second part, I would just run the function in a loop until the result matches the input.

Special Palindrome from hackerrank

I am Solving a Hacker rank problem
https://www.hackerrank.com/challenges/special-palindrome-again/problem
I not able to pass test cases Still my logic is correct
I am able to find palindromes but my code finding an another palindrome which is not listed in explanation it leads to failure of test cases
List lstr=new ArrayList<>();
for(int i=0;i<s.length();i++)
{
for(int j=i+1;j<=s.length();j++)
{
String str=new StringBuilder(s.substring(i,
j)).reverse().toString();
if(s.substring(i, j).equals(str) && s.substring(i, j).length()>1 )
{
lstr.add(str);
}
}
return lstr.size()+s.length();
Input
5 asasd
special palindrome strings {a,s,a,s,d,asa,sas}
Input
7 abcbaba
special palindrome strings {a,b,c,b,a,b,a,bcb,bab,aba} but in above
example my program is finding abcba as palindrome also this leads to
failure of test cases
Special String Again
I gather you're not counting each possible special string.
Try this out, it passes all the cases.
static long substrCount(int n, String s) {
//automatically count each single character as a special string
//thus, the default number of special strings is equal to
//the length of the string
int numOfSpecialStrings = s.length();
//start iterating through the string
for (int i = 0; i < s.length(); i++) {
//to count all special strings, we
//we will restart the counting
//for each iteration.
int numOfRepeat = 0;
//this while loop takes care of the repetitive-character special strings
while (i + 1 < s.length() && s.charAt(i) == s.charAt(i + 1)) {
numOfRepeat++;
i++;
}
//In a repetitive-character string the total of all
//substrings can be calculated using the
//triangular numbers formula.
numOfSpecialStrings += (numOfRepeat * (numOfRepeat + 1) / 2);
//Now we have to take care of the special strings that
//are mirrored, but they can be e.g aba but can also
//be aabaa and etc. How do we take care of these if
//we can have an infinite amount of these?
//introduce a pointer that will take care of the substrings
//with minimum size 3 - it will check the i-1,i and i+1.
int pointer = 1;
//So, while we do not violate the boundary conditions of the loop
//we have increase the size of substring we are checking.
//E.g if pointer = 1 and we check
// aba, b is i, so i-pointer is a and i+pointer is a.
//We can see that a = a, so now we will expand our pointer
// and increment the numberOfSpecialStrings and
//will now reach i-2, i, i+2 and so on.
//This will go on until we either reach the end of the string, or the
//condition is not satisfied.
while (i - pointer >= 0 && i + pointer < s.length() &&
s.charAt(i + pointer) == s.charAt(i - 1)
&& s.charAt(i - pointer) == s.charAt(i - 1)) {
numOfSpecialStrings++;
pointer++;
}
}
System.out.println(numOfSpecialStrings);
return numOfSpecialStrings;
}

Is the for loop the reason for OutOfMemoryError? (Eclipse) [duplicate]

This question already has answers here:
What is an OutOfMemoryError and how do I debug and fix it
(4 answers)
Closed 5 years ago.
Hi I'm writing a program that parses a String into individual component as but when I try to test it out, I get an Out of Memory Error. I feel as if my for/while loops are infinite but I can't seem to find the reason why.
//for loop to loop through char of string
for(int i=0; i<expressionString.length(); i++) {
//cast char into ascii int
int ascii = (int) charAt(i);
//appending to token if one of singly operator symbols: *,/,(,),[,]
if(ascii == 40 || ascii == 41 || ascii == 42 || ascii == 47 || ascii == 91 || ascii == 93){
token.append((char) ascii);
tokenList.add(token.toString());
} //append if +, -
else if(ascii == 43 || ascii == 45) {
token.append((char) ascii);
//check next char if + or /, if so append to token again
int nextChar = (char) charAt(i+1);
if(nextChar == 43 || nextChar == 45) {
token.append((char) nextChar);
}
tokenList.add(token.toString());
} //appending to token if it's a num
else if ( ascii >= 48 || ascii <=57) {
token.append((char) ascii);
//check if next char is a num
while ((int) charAt(i+1) >= 48 || (int) charAt(i+1) <= 57) {
//increment i in for loop to check
i++;
token.append((int) charAt(i));
}
tokenList.add(token.toString());
}
//
}
Please let me know if this is an error with my code as I can't seem to detech where the problem is. Thank you!
As I indicated in the comments, the fact that you are appending to a StringBuilder without ever removing anything from it is dubious.
StringBuilder is simply a wrapper around a char[], which gets automatically resized when necessary to accommodate the new text that you are trying to append. You can see in the stack trace that the OOM is occurring during one of these automatic resizes.
One solution to this problem is simply to allocate a large enough buffer initially, then the resizes don't need to occur until much more text has been appended to the StringBuilder:
StringBuilder token = new StringBuilder(MAXIMUM_EXPECTED_SIZE);
The problem with this is that it may be hard to determine MAXIMUM_EXPECTED_SIZE; and moreover that you may be wasting a lot of memory most of the time, where you are appending nowhere near that amount of text to the buffer.
It seems like you don't actually want to keep the text in token once you have transferred it to tokenList. You can delete it explicitly from the buffer using:
token.delete(0, token.length());
// or
token.setLength(0);
(Actually, this doesn't delete the data, it just allows subsequent appends to overwrite it)
But this is still wasteful: you don't need the StringBuilder at all.
Consider how you handle the numbers:
if ( ascii >= 48 || ascii <=57) {
token.append((char) ascii);
//check if next char is a num
while ((int) charAt(i+1) >= 48 && (int) charAt(i+1) <= 57) {
// ^^ NB
//increment i in for loop to check
i++;
token.append((int) charAt(i));
}
tokenList.add(token.toString());
}
What you're apparently trying to do here is to append everything between the i-th character (inclusive) and the j-th character (exclusive), where j points either to the end of the string, or to a non-numeric character. So you can do that like this:
if ( ascii >= 48 || ascii <=57) {
int j = i + 1;
//check if next char is a num
while (j < expressionString.length() && charAt(j) >= '0' && charAt(j) <= '9') {
j++;
}
tokenList.add(expressionString.subString(i, j));
i = j;
}
You can do similarly for the other appending of tokens. This just cuts out the "middle man" of the StringBuilder, which obviously avoids problems with it re-allocating its internal buffer.
Here is simplified version of what you are doing in that loop.
public class Main {
public static void main(String[] args) {
String str = "ABCDE";
StringBuilder sb = new StringBuilder();
List<String> list = new ArrayList<>();
for (char c : str.toCharArray()) {
sb.append(c);
list.add(sb.toString()); // <-- Problem! This adds the *entire* contents of the StringBuilder as a new String to the list.
}
System.out.println(list);
}
}
This program prints
[A, AB, ABC, ABCD, ABCDE]
This is because each time we append a char to the StringBuilder, we then add the entire contents of the StringBuilder as a new String to the ArrayList.
Now suppose we replace "ABCDE" with a String of length 1000000, for example we change the first line to
String str = Stream.generate(() -> "A").limit(1000000).collect(Collectors.joining()); // String of length 1000000
We are now trying to create 1000000 String objects of lengths from 1 to 1000000, with predictable results.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at my_package.Main.main(Main.java:17)
How to fix it? It depends on what you are trying to do (and we don't have all the context), but I suspect you don't need both a StringBuilder and a List.

Coding Bat String challenge is failing tests

public int countYZ(String str) {
int count = 0;
for(int i = 0;i<str.length();i++)
if((str.charAt(i)=='y'||str.charAt(i)=='z')&&!(Character.isLetter(i+1)))
count++;
return count;
}
This code is failing the test for this criteria : Given a string, count the number of words ending in 'y' or 'z' -- so the 'y' in "heavy" and the 'z' in "fez" count, but not the 'y' in "yellow" (not case sensitive). We'll say that a y or z is at the end of a word if there is not an alphabetic letter immediately following it. (Note: Character.isLetter(char) tests if a char is an alphabetic letter.)
I tracked it by hand and cannot seem to figure out why it isn't working for many of the tests. For example, in "day fyyyz" it outputs 5 but it should be returning 2. Appreciate the help!
Two things to consider:
As pointed out by UnholySheep, Character.isLetter(i+1) isn't checking the i+1th character in the string. You'd need to use:
Character.isLetter(str.charAt(i+1))
But simply changing that can yield an IndexOutOfBoundsException, because you overrun the end of the string if the string ends with a y or z. So, change the second bit of the condition to
!(i+1 != str.length() && Character.isLetter(str.charAt(i+1)))
This also counts the y or z at the end of the string.
Of course, this can be written as:
i+1 == str.length() || !Character.isLetter(str.charAt(i+1))
which is a bit less awkward.
Overall condition:
if((str.charAt(i) == 'y' || str.charAt(i) == 'z')
&& (i+1 == str.length() || !Character.isLetter(str.charAt(i+1)))
If you want to make it case insensitive, use Character.toLowerCase(str.charAt(i)) before comparing to y and z.
Your function is return the total number of y and z characters in the string instead of the number of words ending in y or z. \\W is a regular expression token to target any non-word character in a string.
public int countYZ(String str) {
String[] words = str.split("\\W");
int tally = 0;
for (String w : words)
tally += (w.endsWith("y") || w.endsWith("z")) ? 1 : 0;
return tally;
}
This is a problem easy to solve with a regex:
String str = "day fyyyz provaz provy drad";
Matcher matcher = Pattern.compile("(?i)\\b\\w+[zy]\\b").matcher(str);
int i = 0;
while (matcher.find()) {
System.out.println(matcher.group());
i++;
}
System.out.println("found " + i);
This code will output:
day
fyyyz
provaz
provy
found 4
This regex (?i)\\b\\w+[zy]\\b means:
(?i) enable match case insensitive
\b match word boundary
\w+ match one or more word character (equal to [a-zA-Z0-9_])
[zy] Match a single character present in the list below [zy]
\b match word boundary
https://regex101.com/r/tLMobI/1

(Java ) problems with myString.charAt(i) > "1" or myString.charAt(i) < "A"

I have a car Object which is supposed to have a characteristic. The characteristic is supposed to have the requirements: starts with two capital letter followed by a number from 1-9 followed by 4 numbers from 0-9.
public void writeCharacteristic(){
System.out.println("write down the characteristic for the car.");
String characteristic = kb.nextLine();
progress = false;
if (characteristic.length() != 7){
System.out.println("The string is not 7 letter/numbers long");
progress = false;
}
for(int i = 0; i < 2; ++i){
if (characteristic.charAt(i) < "A" || characteristic.charAt(i) > "Z"){
System.out.println(" character number " + i + " is invalid");
progress = false;
}
}
if (characteristic.charAt(3) < "1" || characteristic.charAt(3) > "9")
progress = false;
for (int j = 3; j < 7; ++j){
if (characteristic.charAt(j) < 0 || characteristic.charAt(j) > 9)
progress =false;
}
if (progress == false){
System.out.println("characteristic will have the value null.");
characteristic = null;
}
if (progress == true)
car.setCharacteristic(characteristic);
}
I'm having a problem at the lines "if (characteristic.charAt(i) < "A" || characteristic.charAt(i) > "Z"){"
The compiler is saying "The operator < is undefined for the argument type(s) char, String"
Any help is highly appreciated, thanks.
In Java, you can compare a character (char) to a character, but you can't compare a character to a String. charAt returns a character, so you must compare its result to a character.
These are String
"A" "Z" "1" "9"
And these are characters
'A' 'Z' '1' '9'
You can compare a character to an integer (int), but the result may not be what you want. So in the code below:
for (int j = 3; j < 7; ++j){
if (characteristic.charAt(j) < 0 || characteristic.charAt(j) > 9)
0 and 9 should be change to '0' and '9'.
Note: There is another unrelated logic error in your code:
String characteristic = kb.nextLine();
progress = false;
Shouldn't progress be set to true here?
I would certainly check out the other answers on this page re. character comparisons. However, I would perhaps suggest a different approach given:
starts with two capital letter followed by a number from 1-9 followed
by 4 numbers from 0-9
and investigate regular expressions. Something like:
[A-Z]{2}[1-9][0-9]{4}
would satisfy the above requirement.
Use single quotes for chars, double quotes for Strings.
characteristic.charAt(3) < '1'
there is meaning for single and double quotes in java
And for your situation best suits is a regex
Replace the double quotes with single quotes.
You'll also have to put single quotes around the numbers when comparing them with chars, even though the compiler doesn't complain.
Compare like this
characteristic.charAt(3) < '1'
First, you can achieve this goal with regexp:
[A-Z]{2}[1-9][0-9]{4}
(Read Pattern article to know how to use it).
If you want to do it as you started - use singleqoutes instead of doublequotes with characters. e.g. "a" -> 'a'.
If you want to assign value to char use single quote. If it is a String use double quote
char myChar='a';
String myString="a";
so
characteristic.charAt(3) < "1" should change as characteristic.charAt(3) < '1'

Categories