Nick
Nick

Reputation: 23

How can I compute the previous and next chars?

My goal is to return a string that is made up of characters that are 1 before and 1 after each character in the given string in the ASCII table.

For example, dog should return cenpfh.

public String encrypt(String s) {
   if (s.length() == 1) {
       String one = "";
       one += (s.charAt(0)-1) + (s.charAt(0)+1);
       return one;
   } else { .. }
}

This is the code I have so far, and I have no idea how to continue. This doesn't even work for a single character, for example, "172" is returned for "V". Could anyone point me in the right direction? Really stumped right now.

Upvotes: 1

Views: 157

Answers (5)

Alex Salauyou
Alex Salauyou

Reputation: 14338

There is no recursion needed. Just iterate over input chars and collect result in a StringBuilder:

public static String encrypt(String s) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        sb.append((char)(c - 1)).append((char)(c + 1));
    }
    return sb.toString();
}

Note that explicit cast to char is required here, because s.charAt(i) - 1 evaluates to int and without cast is processed by overloaded append(int i) method.


Okay, recursion. Here I split input into 2 halves and process each by using the same encryptRecursively() method, then join results. Each part, when passed to the method, is again split into 2 parts, etc, until size becomes 0 or 1 (thus we get a very simple implementation of Divide and Conquer):

public static String encryptRecursively(String s) {
    if (s.isEmpty())
        return "";
    else if (s.length() == 1) {
        char c = s.charAt(0);
        return new String(new char[] { (char)(c - 1), (char)(c + 1) });
    } else {
        int mid = s.length() / 2;
        return encryptRecursively(s.substring(0, mid)) // process first half of input
             + encryptRecursively(s.substring(mid));   // second half
    }
}

Upvotes: 3

slim
slim

Reputation: 41223

This is fairly tricky stuff for a beginner, but it's good stuff to encounter early and understand.

"" + 172

results in the string "172"

"" + 'V'

results in the string "V".

V and 172 are both represented internally as the same number, so what's the difference? The difference is that the compiler keeps track of the type of the value. 'V' has a type of char. 172 has a type of int.

When you perform an expression using a mixture of types, the compiler has to decide what the type of the result is. This will influence how that result is handled by routines such String concatenation.

As it happens when you subtract an int from a char, the type of the result is an int (the Java spec tells you this).

You can get around it by casting the type to something else.

  • (char) 172 is 'V'
  • (int) 'V' is 172.

So:

  one += (char) (s.charAt(0)-1) + (char)(s.charAt(0)+1);

Recursion is a separate topic. Recursion works for handling sequences (a String is a sequence of characters) when you can do a chunk of work on a part of the sequence, leaving you with a new, shorter sequence, that you can do the same job on -- until finally you have the simplest case, which you simply return.

The simplest case, the one where the method returns without recursively calling itself, is called the terminating case. You need at least one terminating case, otherwise the recursive method will keep calling itself until Java runs out of memory for storing method states.

You have chosen your terminating case as being a one char string. I would instead pick a zero length string:

 public void encrypt(String s) {
     if("".equals(s)) {
         return "";
     } else {
         // do something with a string that's guaranteed >1 char
     }

Now you just have to work out how to replace that comment, by using up the guaranteed one char, and calling encrypt() to handle what's left.

 public void encrypt(String s) {
     if("".equals(s)) {
         return "";
     } else {
         return encodeOneChar(s.charAt(0)) +
              encrypt(x.substring(1));
     }
 }

That's it:

  • when called with an empty string, encode() returns an empty string.
  • when called with a non-empty string, encode processes the first char and calls itself to deal with a string that's one shorter.

Some languages, like Haskell, make recursion easier to understand, by representing the cases as pattern matching statements.

encrypted "" == ""
encrypted s == encryptChar(head(s)) + encrypted(tail(s))

(that's not real Haskell, but it illustrates the pattern.)

Recursion isn't quite as easy to read in Java, but the pattern is still there. if(terminating case) { return answer for terminating case } else { calculate something, call self, return combined result }

I'll leave you to implement private String encodeOneChar(char c)


Note that recursion isn't a good solution to your actual task in Java -- except in the sense that your goal is to understand recursion.

Upvotes: 3

Nikolas
Nikolas

Reputation: 44398

You don't need a recursion but a simple for-loop through all chars in the word. You get the value of char with s.charAt(...), but you have to convert it back to char for the desired resut. Append them to StringBuilder and return its String as the output of the method:

public static String encrypt(String s) {      
    StringBuilder sb = new StringBuilder();
    for (int i=0; i<s.length(); i++) {
        char a = (char)(s.charAt(i)-1);
        char b = (char)(s.charAt(i)+1);
        sb.append(a).append(b);
    }
    return sb.toString();
}

Here is the recursion method as demanded:

public static String encrypt(String s) {
    if (s.length() == 1) {
        StringBuilder sb = new StringBuilder();
        char a = (char)(s.charAt(0)-1);
        char b = (char)(s.charAt(0)+1);        
        return sb.append(a).append(b).toString();
    } else {
        return encrypt(s.substring(0, 1)) + encrypt(s.substring(1, s.length()));
}

Upvotes: 1

Iłya Bursov
Iłya Bursov

Reputation: 24146

If you have to use recursion, you can try something like that:

public static String encrypt(final String s) {
    if (s.length() == 1) {
        final char c = s.charAt(0);
        return new String(new char[] {(char) (c - 1), (char) (c + 1)});
    } else {
        return encrypt(s.substring(0, 1)) + encrypt(s.substring(1));
    }
}

the main idea here:

  1. if input length is 1 symbol - we know what to do and just perform "encryption"
  2. if input length is more than 1 symbol - we split string into "first symbol" and "rest of the string" and call itself on both parts

Upvotes: 3

brso05
brso05

Reputation: 13222

You can try this:

one += (char)(((int)s.charAt(0))-1);
one += (char)(((int)s.charAt(0))+1);

Input:

V

Output:

UW

Upvotes: 0

Related Questions