Reputation: 25
I am using StringBuilder to change a String input and shift it depending on input. This is for the META coding practice website and I am running into an issue with their two Test cases. One is passing and the other is not.
The expected output is stuvRPQrpq-999.@
and the input is abcdZXYzxy-999.@
with a shift of 200
.
Here is my code
String rotationalCipher(String input, int rotationFactor) {
// Write your code here
int shift = rotationFactor % 26;
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
if (character >= 'a' && character <= 'z') {
character = (char) (character + shift);
if (character > 'z') {
character = (char) (character + 'a' - 'z' - 1);
}
output.append(character);
} else if (character >= 'A' && character <= 'Z') {
character = (char) (character + shift);
if (character > 'Z') {
character = (char) (character + 'A' - 'Z' - 1);
}
output.append(character);
} else if (character >= '0' && character <= '9') {
character = (char) (character + shift);
if (character > '9') {
character = (char) (character + '0' - '9' - 1);
}
output.append(character);
} else {
output.append(character);
}
}
return output.toString();
}
My issue is that I am somehow outputting AAA
instead of 999
as far as I can tell from tracing my algo seems solid. I looked through JAVA docs StringBuilder page to see if there was any issue with how I was using it. As far as I can tell it should be good to go.
Could anyone lend me an idea of why my output is the way it is?
Here is the test cases code:
String input_1 = "All-convoYs-9-be:Alert1.";
int rotationFactor_1 = 4;
String expected_1 = "Epp-gsrzsCw-3-fi:Epivx5.";
String output_1 = rotationalCipher(input_1, rotationFactor_1);
check(expected_1, output_1);
String input_2 = "abcdZXYzxy-999.@";
int rotationFactor_2 = 200;
String expected_2 = "stuvRPQrpq-999.@";
String output_2 = rotationalCipher(input_2, rotationFactor_2);
check(expected_2, output_2);
Upvotes: 0
Views: 106
Reputation: 347244
Check your maths
200 % 26 = 18 (shift)
'9' + 18 = 57 + 18 = 75 ('K')
75 + '0' = 75 + 48 = 123 ('{')
123 - '9' = 123 - 57 = 66 ('B')
66 - 1 = 65 ('A')
Now, the problem is, between '9' and 'A' there are 7 other characters, so character = (char) (character + ('0' - '9') - 1);
would have to become character = (char) (character + ('0' - '9') - 9);
to shift 9
back to 9
, but that would screw up you other test case
I don't think ASCII manipulation is the way to go here, as there are characters in-between the digits and the upper and lower cased characters which are going to mess things up as the rotation increases.
In fact, for the digits, you really want to rotate using a factor % 10
instead.
A different approach would be to generate a list of characters and apply a shift to those instead. Now if I was doing this, I'd use List
and Collections
, but lets assume you can't do that for second, instead, we're going to need to apply a shift to an array, for example...
public String[] rotate(String[] original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String[] positiveRotate(String[] original, int offset) {
String[] results = new String[original.length];
int count = original.length - offset;
System.arraycopy(original, count, results, 0, offset);
System.arraycopy(original, 0, results, offset, count);
return results;
}
public String[] negativeRotate(String[] original, int offset) {
String[] results = new String[original.length];
System.arraycopy(original, offset, results, 0, original.length - offset);
System.arraycopy(original, 0, results, original.length - offset, offset);
return results;
}
Now, this has two different methods, one of a "positive" (or "right" shift) and one for a "negative" (or "left" shift). During testing, I found that the you want to "left shift" the array.
Next, we need what we want to shift over...
private String[] digits = "0123456789".split("");
private String[] characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
I've cheated here, you may need to create the array by long hand, but I can't be bothered typing it out.
Please note
String
array instead of a char
array, not hard to change, but I'm been lazyString
directly, using things like contains
and split
to perform the shiftingAnd then the rotational cipher might look something like...
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String[] shiftedDigits = rotate(digits, -(rotationFactor % 10));
String[] shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = indexOf(value, digits)) > -1) {
output.append(shiftedDigits[index]);
} else if ((index = indexOf(value, characters)) > -1) {
output.append(shiftCharacters[index]);
} else if ((index = indexOf(value.toUpperCase(), characters)) > -1) {
output.append(shiftCharacters[index].toLowerCase());
} else {
output.append(value);
}
}
return output.toString();
}
protected int indexOf(String value, String[] array) {
for (int index = 0; index < array.length; index++) {
if (array[index].equals(value)) {
return index;
}
}
return -1;
}
Then you could just execute it something like...
System.out.println(" --> All-convoYs-9-be:Alert1.");
System.out.println(" Got " + rotationalCipher("All-convoYs-9-be:Alert1.", 4));
System.out.println("Want Epp-gsrzsCw-3-fi:Epivx5.");
System.out.println("");
System.out.println(" --> abcdZXYzxy-999.@");
System.out.println(" Got " + rotationalCipher("abcdZXYzxy-999.@", 200));
System.out.println("Want stuvRPQrpq-999.@");
Which outputs
--> All-convoYs-9-be:Alert1.
Got Epp-gsrzsCw-3-fi:Epivx5.
Want Epp-gsrzsCw-3-fi:Epivx5.
--> abcdZXYzxy-999.@
Got stuvRPQrpq-999.@
Want stuvRPQrpq-999.@
As I said above, you could just use a String
instead of an array, in which case it might look something more like...
private String digits = "0123456789";
private String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String shiftedDigits = rotate(digits, -(rotationFactor % 10));
String shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = digits.indexOf(value)) > -1) {
output.append(shiftedDigits.charAt(index));
} else if ((index = characters.indexOf(value)) > -1) {
output.append(shiftCharacters.charAt(index));
} else if ((index = characters.indexOf(value.toUpperCase())) > -1) {
output.append(Character.toLowerCase(shiftCharacters.charAt(index)));
} else {
output.append(value);
}
}
return output.toString();
}
public String rotate(String original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String positiveRotate(String original, int offset) {
String prefix = original.substring(original.length() - offset);
String suffix = original.substring(0, original.length() - offset);
return prefix + suffix;
}
public String negativeRotate(String original, int offset) {
String prefix = original.substring(offset);
String suffix = original.substring(0, offset);
return prefix + suffix;
}
Upvotes: 1