0

I have to handle some strings, I should put them N positions to left to organize the string.

Here's my code for while:

private String toLeft() {
    String word = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Example
    byte lpad = 2; // Example
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < word.length(); i++) {
        sb.append((char) (word.charAt(i) - lpad));
    }
    return sb.toString();
}

It's working for inputs that don't have to move many times...

So, the problem is that when the number N of positions to move is a little bit large (like 10), it returns me non letters, like in the example below, what can I do to prevent it?

Ex.: ABCDEFGHIJKLMNOPQRSTUVWXYZ if I move each char 10 positions to left it returns 789:;<=>?@ABCDEFGHIJKLMNOP while it must return QRSTUVWXYZABCDEFGHIJKLMNOP.

Some inputs and their expected outputs:

  • VQREQFGT // 2 positions to left == TOPCODER
  • ABCDEFGHIJKLMNOPQRSTUVWXYZ // 10 positions to left == QRSTUVWXYZABCDEFGHIJKLMNOP
  • LIPPSASVPH // 4 positions to left == HELLOWORLD
developer033
  • 24,267
  • 8
  • 82
  • 108
  • You are getting the characters after Z. You need to do some add/subtract/modulo math to get the right number. See [here](http://stackoverflow.com/questions/35242376/java-caesar-cipher-code) – Laurel Apr 03 '16 at 00:47

3 Answers3

3

I think you have misunderstood what your (homework?) requirements are asking you to do. Lets look at your examples:

VQREQFGT // 2 positions to left == TOPCODER

Makes sense. Each character in the output is two characters before the corresponding input. But read on ...

ABCDEFGHIJKLMNOPQRSTUVWXYZ // 10 positions to left == QRSTUVWXYZABCDEFGHIJKLMNOP

Makes no sense (literally). The letter Q is not 10 characters before A in the alphabet. There is no letter in the alphabet that is before A in the alphabet.

OK so how do you get from A to Q in 10 steps?

Answer ... you wrap around!

A, Z, Y, X, W, V, U, T, S, R, Q ... 10 steps: count them.

So what the requirement is actually asking for is N characters to the left with wrap around. Even if they don't state this clearly, it is the only way that the examples "work".

But you just implemented N characters to the left without wrap around. You need to implement the wrap around. (I won't show you how, but there lots of ways to do it.)


There's another thing. The title of the question says "Decrement only letters" ... which implies to me that your requirement is saying that characters that are not letters should not be decremented. However, in your code you are decrementing every character in the input, whether or not it is a letter. (The fix is simple ... and you should work it out for yourself.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

what can I do to prevent it?

You make it wrap around.
If you want a value to go from 10 to 19 and then start at 10 again, in each iteration you subtract 10, increase by one, take the remainder of that divided by 20, and add 10 again.

Only here, 10 is 'A', 19 is Z, and instead of increasing by one, we add or subtract n.

private String toLeft() {
    String word = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Example
    byte lpad = 10; // Example
    StringBuilder sb = new StringBuilder();
    int n = -lpad;                  // I'm using n here to I can use '+ n' below
    for (int i = 0; i < word.length(); i++) {
        int shifted = word.charAt(i) - 'A' + n;
        shifted %= ('Z' - 'A' + 1); // This is for positive n
        while(shifted < 0)          // And this for negative ones
        {
            shifted += ('Z' - 'A' + 1);
        }
        sb.append((char)(shifted + 'A'));
    }
    return sb.toString();
}
Siguza
  • 21,155
  • 6
  • 52
  • 89
0

Please read @StephenC's excellent answer about wrap-around. In short, you don't shift left, you rotate left, such that BAZY. When you rotate, you wrap around to the other end.

So, for letters you want A-Z to rotate. The easiest rotation method is using modulus (%).

Your logic will be as follows:

  1. Convert letters A-Z into numbers 0-25: n = ch - 'A'
  2. Apply shift and wrap around. Since you're shifting left, you're subtracting from the number, so to prevent negative numbers, you start by shifting a full cycle to the right: n = (n + 26 - shift) % 26
  3. Convert numbers back to letters: ch = (char)(n + 'A')

Here is the code:

private static String rotateLeft(String text, int count) {
    char[] buf = text.toCharArray();
    for (int i = 0; i < buf.length; i++)
        buf[i] = (char)((buf[i] - 'A' + 26 - count) % 26 + 'A');
    return new String(buf);
}

Of course, you should validate input, and test your code:

private static String rotateLeft(String text, int count) {
    char[] buf = text.toCharArray();
    if (count <= 0 || count >= 26)
        throw new IllegalArgumentException("Invalid count: " + count);
    for (char ch : buf)
        if (ch < 'A' || ch > 'Z')
            throw new IllegalArgumentException("Invalid character: " + ch);
    for (int i = 0; i < buf.length; i++)
        buf[i] = (char)((buf[i] - 'A' + 26 - count) % 26 + 'A');
    return new String(buf);
}

public static void main(String[] args) {
    System.out.println(rotateLeft("VQREQFGT", 2));
    System.out.println(rotateLeft("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10));
    System.out.println(rotateLeft("LIPPSASVPH", 4));
}

OUTPUT

TOPCODER
QRSTUVWXYZABCDEFGHIJKLMNOP
HELLOWORLD
Community
  • 1
  • 1
Andreas
  • 154,647
  • 11
  • 152
  • 247