Rotating a string with ASCII in Java - java

I'm doing some practice on HackerRank and I'm stuck on a problem. The problem is:
Julius Caesar protected his confidential information by encrypting it using a cipher. Caesar's cipher shifts each letter by a number of letters. If the shift takes you past the end of the alphabet, just rotate back to the front of the alphabet. In the case of a rotation by 3, w, x, y and z would map to z, a, b and c.
Original alphabet: abcdefghijklmnopqrstuvwxyz
Alphabet rotated +3: defghijklmnopqrstuvwxyzabc
Example
input:
11
middle-Outz
2
output:
okffng-Qwvb
I pass this test case, but I don't pass the rest of the test cases and it won't tell me which ones. What am I missing in my logic?
public static String caesarCipher(String s, int k) {
String newString = "";
// Write your code here
for (int i=0; i<s.length(); i++) {
char currentChar = s.charAt(i);
int currentAscii = currentChar;
if (currentAscii <65|| currentAscii>122 || (currentAscii>90 && currentAscii < 97)) {
newString += currentChar;
} else {
currentAscii = currentChar+k;
if (currentAscii > 122) {
currentAscii = Math.abs(currentAscii-26);
}
newString += (char) currentAscii;
}
}
return newString;
}
.

Here is an ASCII chart.
Your rotate algorithm is:
If the value is below 65 (less than 'A' in that chart), or above 122 (more than 'z' in that chart), do nothing to the character and just copy it verbatim.
In all other cases, add k, e.g. 11. This goes wrong, immediately. Note how for example [ is in between 65 and 122. Adding 11 to [ is nonsensical. [ should obviously be treated the same as, say, < is treated (which is less than 65).
Of course, adding 11 isn't what 'rotating' is all about: If adding 11 pushes it 'beyond' the z or Z, you want to snap around back to the a or A. Your code does that, but only if the end result is above 'z'. You forgot about 'Z'.
For example, adding 1 to Z (Which is value 90) gets you [ (the character with value 91). Given that 91 is not above 122, you don't modify this at all.
Add more test cases, especially involving capitals, preferably Z. You'll see the problem immediately.
The solution involves getting rid of the 65/122 dichotomy; instead it's 2 ranges: 65-90 and 97-122. For what it is worth, int a = (int) 65; and int a = (int) 'a'; is exactly the same code, and the latter is far more readable, so you might want to use that. There is no need to write any actual number in this code anywhere, except possibly '26'.

Related

Strange Java Behavior [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed last year.
Improve this question
Firstly, this question came about because I was trying to make a hello world program (the first main method commented out) as convoluted as possible. I tried to rewrite some of it to make it more readable but maintain the strange behavior I am addressing.
Specifically, in the for loop I am trying to convert the strings to floats, then truncate the floats to ASCII values, and then lastly to chars that say "Hello, World!" The problem is that inside the conditional, in the for loop, if you have it simply be (i == 0) it will capitalize the h just fine but when you add
(i == 6) it acts really strange and ends up printing chars inconsistently such as "Hello,d!", "Hello,!" and "Hello,rld!" (among others). Can anyone explain this behavior to me? Thank you.
import java.util.Scanner;
class helloWorld {
// public static void main(String[] args) {
// System.out.println("Hello, World!");
// }
public static void main(String[] args) {
//This array is just soon to be truncated ASCII vals
String[] nums = {"104.367", "101.432", "108.43276", "108.1", "111.43", "44.231", "32.12", "119.32", "111.24", "114.37", "108.2", "100.52", "33.237", "10.4"};
//Empty "Hello, World!" string
String message = "";
//This is to store the converted ASCII values
char[] chars = new char[nums.length];
//this is just the difference in ASCII vals to go from lower to uppercase letters
int toUpperCase = 32;
for (int i = 0; i < nums.length; i++){
//i == 6 || i == 0 because I want the w and h to be capitalized
//i == 0 capitalizes the 'h' and prints as expected but adding "i == 6 ||" to the condition causes it to break and print chars inconsistently
if (i == 6 || i == 0) {
//converts from float to int (truncates decimals to the normal ASCII val) and then from the ASCII value to the char
chars[i] = (char)((int)((Float.parseFloat(nums[i])) - toUpperCase));
} else {
chars[i] = (char)((int)(Float.parseFloat(nums[i])));
}
System.out.print(chars[i]);
}
System.out.print(message);
}
}
tl;dr
The bug is actually a minor one, you used i == 6 but instead you should have used i == 7.
Index
Let's break down what happens and why. To start off, the source string you are reading from (even though you made it floats, but doesn't matter) is Hello, World!, the indices for your array are as follows:
// _ denotes the space
h e l l o , _ w o r l d !
0 1 2 3 4 5 6 7 8 9 10 11 12
Note that 6 is actually the index of the space and not the w, which you wanted to capitalize.
NUL character
Next up, you subtract 32 from the ASCII value of the space. Fun fact, the ASCII value of that is 32 as well:
System.out.println((int) ' '); // 32
So your subtraction results in 0. The char that has the ASCII value 0 is the so called null character (not to confuse with the Java keyword null), which is a special control character with no visual representation. It can also be written as '\0'.
System.out.println((char) 0);
So when you told your console to print the null char, it might start doing funny things. Although, the consoles I tested with, simply skip the character and print Hello,world! consistently.
If you want to try it out with a minimal example, use this one:
System.out.println("Hello,\0world!");
which is the string that your code produced.
ASCII table
While at it, here is the ASCII table with the null character and space highlighted (from Wikipedia):
You can also see that the first 32 characters are all control characters without a direct visual representation. In fact, try out this one:
System.out.print((char) 7);
depending on your console, you might hear a beep sound now.

string a to i problem on leetcode my solution fails incase of 2 consecutive signs

problem link: https://leetcode.com/problems/string-to-integer-atoi/
i have implemented my solution but it fails for testcase like "-+42"since 2 consecutive signs appear in string. what changes do i need to do in my code, and where am i wrong? help would be appreciated
my code:
class Solution {
public int myAtoi(String s) {
s=s.trim();
char sign='+';
if (s.length()==0) return 0;
String digit="";
for(int i=0;i<s.length();i++)
{
//checking for sign
if(s.charAt(i)=='-'||s.charAt(i)=='+')
sign=s.charAt(i);
//checking for all non digit characters
else if((s.charAt(i)>='a'&& s.charAt(i)<='z')||(s.charAt(i)>='A' && s.charAt(i)<='Z')||(s.charAt(i)==' ')||(s.charAt(i)=='.')||(s.charAt(i)=='-')||(s.charAt(i)=='+'))
break;
else{
digit=digit+s.charAt(i);
}
}
if(digit=="") return 0;
//System.out.print(sign);
try{
int n=(Integer.parseInt(digit));
//int a=Integer.parseInt(digit);
System.out.print(sign);
if(sign=='-')
return (-n);
return n;
}
//check for out of range for integer
catch(NumberFormatException e)
{
if(sign=='-')
{
//
int n=Integer.MIN_VALUE;
return n;}
else
{
int n=Integer.MAX_VALUE;
return n;
}
}
}
}
Er, just.. do what the exercise says? Not sure what you want to hear from SO here. I guess: "Can you regurgitate the question to me". Can do!
Your algorithm isn't doing anything like what the exercise spells out you need to do. You are triggering on a sign character appearing anywhere, and your code scans for letters, all things which the exercise doesn't mention. At all. The exercise mentions the concept of a 'not a digit' and the first character (after getting rid of spaces, which you already did with a trim() invocation).
So, do what the exercise says:
Instead of if (s.charAt(i) == '-' your code needs to involve s.charAt(0) - there are only 3 options:
The trimmed string starts with a +. Set the sign to +, and start looking at digits from position 1.
The trimmed string starts with a -. Set the sign to -, and start looking at digits from position 1.
Neither. Set the sign to + and start looking at digits from position 0.
Your s.charAt(i) >= 'a' is also wrong. You're not looking for a letter. You're looking for anything that isn't a digit. + isn't a letter. Nevertheless, in input -5+10, you're supposed to return -5. In -+10 you're supposed to return 0. (Because that's like -hello - the negative value of no digits, which is minus zero, which is just zero).
I think the intent of the exercise is not to use Integer.parseInt whatsoever.
Try this algorithm:
if your character is >= '0' and <= '9', you can subtract '0' to get the actual digit in integer form: char c = '5'; int y = c - '0'; System.out.println(y); would print 5.
multiply your existing input by 10, then add the digit you have. In other words, for text input "29", first you read a 2, you multiply the number you're working on (0, at first) to 10 (still 0), then you add 2, giving you 2. Then you get another digit, so you multiply what you ahve so far by 10 (giving you 20), and add the digit value, getting you the integer 29. No need for parseInt.
You now you need to clamp when your working total flips signs on you. So, if all of a sudden your input is negative (as you won't yourself make it negative until the very end), you know you have to clamp and return Integer.MIN_VALUE or Integer.MAX_VALUE, depending on the sign value.

What happens when if statement goes true (in this code)?

There is a problem in codingbat.com which you're supposed to remove "yak" substring from the original string. and they provided a solution for that which I can't understand what happens when the if statement goes true!
public String stringYak(String str) {
String result = "";
for (int i=0; i<str.length(); i++) {
// Look for i starting a "yak" -- advance i in that case
if (i+2<str.length() && str.charAt(i)=='y' && str.charAt(i+2)=='k') {
i = i + 2;
} else { // Otherwise do the normal append
result = result + str.charAt(i);
}
}
return result;
}
It just adds up i by 2 and what? When it appends to the result string?
Link of the problem:
https://codingbat.com/prob/p126212
The provided solution checks for all single characters in the input string. For this i is the current index of the checked character. When the current char is not a y and also the (i+2) character is not a k the current char index is advanced by 1 position.
Example:
yakpak
012345
i
So here in the first iteration the char at i is y and i+2 is a k, so we have to skip 3 chars. Keep in mind i is advanced by 1 everytime. So i has to be increased by 2 more. After this iteration i is here
yakpak
012345
i
So now the current char is no y and this char will get added to the result string.
But it's even simpler in Java as this functionality is build in with regex:
public String stringYak(String str) {
return str.replaceAll("y.k","");
}
The . means every char.
If i is pointing at a y and there is as k two positions down, then it wants to skip the full y*k substring, so it add 2 to i so i now refers to the k. WHen then loop continues, i++ will skip past the k, so in effect, the entire 3-letter y*k substring has been skipped.

Converting letters to alphabet position with two JLists

I'm trying to replace all words (alphabet letters) from JList1 to the number corresponding its place in the alphabet to JList2 with the press of the Run button. (ex. A to 01) And if it's not an English alphabet letter then leaving it as it is. Capitalization doesn't matter (a and A is still 01) and spaces should be kept.
For visual purposes:
"Apple!" should be converted to "0116161205!"
"stack Overflow" to "1920010311 1522051806121523"
"über" to "ü020518"
I have tried a few methods I found on here, but had zero clue how to add the extra 0 in front of the first 9 letters or keep the spaces. Any help is much appreciated.
Here is a solution :
//Create a Map of character and equivalent number
Map<Character, String> lettersToNumber = new HashMap<>();
int i = 1;
for(char c = 'a'; c <= 'z'; c++) {
lettersToNumber.put(c, String.format("%02d", i++));
}
//Loop over the characters of your input and the corresponding number
String result = "";
for(char c : "Apple!".toCharArray()) {
char x = Character.toLowerCase(c);
result+= lettersToNumber.containsKey(x) ? lettersToNumber.get(x) : c;
}
Input, Output
Apple! => 0116161205!
stack Overflow => 1920010311 1522051806121523
über => ü020518
So given...
(ex. A to 01) And if it's not an English alphabet letter then leaving it as it is. Capitalization doesn't matter (a and A is still 01) and spaces should be kept.
This raises some interesting points:
We don't care about non-english characters, so we can dispense with issues around UTF encoding
Capitalization doesn't matter
Spaces should be kept
The reason these points are interesting to me is it means we're only interested in a small subset of characters (1-26). This immediately screams "ASCII" to me!
This provides an immediate lookup table which doesn't require us to produce anything up front, it's immediately accessible.
A quick look at any ascii table provides us with all the information we need. A-Z is in the range of 65-90 (since we don't care about case, we don't need to worry about the lower case range.
But how does that help us!?
Well, this now means the primary question becomes, "How do we convert a char to an int?", which is amazingly simple! A char can be both a "character" and a "number" at the same time, because of the ASCII encoding support!
So if you were to print out (int)'A', it would print 65! And since all the characters are in order, we just need to subtract 64 from 65 to get 1!
That's basically your entire problem solved right there!
Oh, okay, you need to deal with the edge cases of characters not falling between A-Z, but that's just a simple if statement
A solution based on the above "might" look something like...
public static String convert(String text) {
int offset = 64;
StringBuilder sb = new StringBuilder(32);
for (char c : text.toCharArray()) {
char input = Character.toUpperCase(c);
int value = ((int) input) - offset;
if (value < 1 || value > 25) {
sb.append(c);
} else {
sb.append(String.format("%02d", value));
}
}
return sb.toString();
}
Now, there are a number of ways you might approach this, I've chosen a path based on my understanding of the problem and my experience.
And based on your example input...
String[] test = {"Apple!", "stack Overflow", "über"};
for (String value : test) {
System.out.println(value + " = " + convert(value));
}
would produce the following output...
Apple! = 0116161205!
stack Overflow = 1920010311 1522051806121523
über = ü020518

How do I check if a char is a vowel?

This Java code is giving me trouble:
String word = <Uses an input>
int y = 3;
char z;
do {
z = word.charAt(y);
if (z!='a' || z!='e' || z!='i' || z!='o' || z!='u')) {
for (int i = 0; i==y; i++) {
wordT = wordT + word.charAt(i);
} break;
}
} while(true);
I want to check if the third letter of word is a non-vowel, and if it is I want it to return the non-vowel and any characters preceding it. If it is a vowel, it checks the next letter in the string, if it's also a vowel then it checks the next one until it finds a non-vowel.
Example:
word = Jaemeas then wordT must = Jaem
Example 2:
word=Jaeoimus then wordT must =Jaeoim
The problem is with my if statement, I can't figure out how to make it check all the vowels in that one line.
Clean method to check for vowels:
public static boolean isVowel(char c) {
return "AEIOUaeiou".indexOf(c) != -1;
}
Your condition is flawed. Think about the simpler version
z != 'a' || z != 'e'
If z is 'a' then the second half will be true since z is not 'e' (i.e. the whole condition is true), and if z is 'e' then the first half will be true since z is not 'a' (again, whole condition true). Of course, if z is neither 'a' nor 'e' then both parts will be true. In other words, your condition will never be false!
You likely want &&s there instead:
z != 'a' && z != 'e' && ...
Or perhaps:
"aeiou".indexOf(z) < 0
How about an approach using regular expressions? If you use the proper pattern you can get the results from the Matcher object using groups. In the code sample below the call to m.group(1) should return you the string you're looking for as long as there's a pattern match.
String wordT = null;
Pattern patternOne = Pattern.compile("^([\\w]{2}[AEIOUaeiou]*[^AEIOUaeiou]{1}).*");
Matcher m = patternOne.matcher("Jaemeas");
if (m.matches()) {
wordT = m.group(1);
}
Just a little different approach that accomplishes the same goal.
Actually there are much more efficient ways to check it but since you've asked what is the problem with yours, I can tell that the problem is you have to change those OR operators with AND operators. With your if statement, it will always be true.
So in event anyone ever comes across this and wants a easy compare method that can be used in many scenarios.
Doesn't matter if it is UPPERCASE or lowercase. A-Z and a-z.
bool vowel = ((1 << letter) & 2130466) != 0;
This is the easiest way I could think of. I tested this in C++ and on a 64bit PC so results may differ but basically there's only 32 bits available in a "32 bit integer" as such bit 64 and bit 32 get removed and you are left with a value from 1 - 26 when performing the "<< letter".
If you don't understand how bits work sorry i'm not going go super in depth but the technique of
1 << N is the same thing as 2^N power or creating a power of two.
So when we do 1 << N & X we checking if X contains the power of two that creates our vowel is located in this value 2130466. If the result doesn't equal 0 then it was successfully a vowel.
This situation can apply to anything you use bits for and even values larger then 32 for an index will work in this case so long as the range of values is 0 to 31. So like the letters as mentioned before might be 65-90 or 97-122 but since but we keep remove 32 until we are left with a remainder ranging from 1-26. The remainder isn't how it actually works, but it gives you an idea of the process.
Something to keep in mind if you have no guarantee on the incoming letters it to check if the letter is below 'A' or above 'u'. As the results will always be false anyways.
For example teh following will return a false vowel positive. "!" exclamation point is value 33 and it will provide the same bit value as 'A' or 'a' would.
For starters, you are checking if the letter is "not a" OR "not e" OR "not i" etc.
Lets say that the letter is i. Then the letter is not a, so that returns "True". Then the entire statement is True because i != a. I think what you are looking for is to AND the statements together, not OR them.
Once you do this, you need to look at how to increment y and check this again. If the first time you get a vowel, you want to see if the next character is a vowel too, or not. This only checks the character at location y=3.
String word="Jaemeas";
String wordT="";
int y=3;
char z;
do{
z=word.charAt(y);
if(z!='a'&&z!='e'&&z!='i'&&z!='o'&&z!='u'&&y<word.length()){
for(int i = 0; i<=y;i++){
wordT=wordT+word.charAt(i);
}
break;
}
else{
y++;
}
}while(true);
here is my answer.
I have declared a char[] constant for the VOWELS, then implemented a method that checks whether a char is a vowel or not (returning a boolean value). In my main method, I am declaring a string and converting it to an array of chars, so that I can pass the index of the char array as the parameter of my isVowel method:
public class FindVowelsInString {
static final char[] VOWELS = {'a', 'e', 'i', 'o', 'u'};
public static void main(String[] args) {
String str = "hello";
char[] array = str.toCharArray();
//Check with a consonant
boolean vowelChecker = FindVowelsInString.isVowel(array[0]);
System.out.println("Is this a character a vowel?" + vowelChecker);
//Check with a vowel
boolean vowelChecker2 = FindVowelsInString.isVowel(array[1]);
System.out.println("Is this a character a vowel?" + vowelChecker2);
}
private static boolean isVowel(char vowel) {
boolean isVowel = false;
for (int i = 0; i < FindVowelsInString.getVowel().length; i++) {
if (FindVowelsInString.getVowel()[i] == vowel) {
isVowel = true;
}
}
return isVowel;
}
public static char[] getVowel() {
return FindVowelsInString.VOWELS;
}
}

Categories