In der Serie "Named Problems" stelle ich dir klassische, namentlich bekannte Algorithmus-Probleme vor, die seit Jahrzehnten in Ausbildung, Studium und Interviews verwendet werden. Anhand verständlicher Problemstellungen wie FizzBuzz, Türme von Hanoi oder dem Sieb des Eratosthenes lernst du hier, algorithmisch zu denken, Lösungsstrategien zu entwickeln und diese sauber in Java umzusetzen.


ROT13 ist eine bekannte Buchstaben-Verschiebung: Jeder Buchstabe wird um 13 Stellen im Alphabet gedreht. Das klingt nach Spielerei, ist aber ein gutes Einsteiger-Problem, um Transformationen sauber umzusetzen.

Warum ist ROT13 bekannt? Weil es extrem einfach ist, aber trotzdem typische Stolperfallen hat: Groß/Kleinschreibung, nur A-Z/a-z verändern, alles andere unverändert lassen.

In diesem Artikel baust du zwei Java-Lösungen. Variante A ist bewusst straightforward. Variante B ist besser wiederverwendbar, weil du die Transformation als eigenständigen Baustein kapselst.

 

Problemstellung

Schreibe eine Funktion, die einen Text nach ROT13 transformiert.

  • Nur Buchstaben A-Z und a-z werden verschoben.

  • Groß- und Kleinschreibung bleibt erhalten.

  • Alle anderen Zeichen (Leerzeichen, Punkte, Ziffern) bleiben unverändert.

  • ROT13 ist sein eigenes Inverses: zweimal anwenden ergibt wieder den Originaltext.

  • Mini-Beispiel

  • Hello wird zu Uryyb

  • Uryyb wird zu Hello

 

Die Idee dahinter

Das Kernkonzept ist Transformation: Du nimmst einen Eingabe-String und erzeugst daraus einen Ausgabe-String, Zeichen für Zeichen. Und du willst Wiederverwendbarkeit: Die Logik sollte so gebaut sein, dass du sie später leicht in anderen Problemen einsetzen kannst (z.B. Caesar-Cipher allgemein, Filter, Normalisierung).

So kannst du dir die Verschiebung vorstellen:

  • Alphabet hat 26 Buchstaben.
  • Du addierst 13 auf die Position eines Buchstabens.
  • Wenn du über Z (oder z) hinaus kommst, wickelst du wieder zum Anfang.

 

Schritt für Schritt zur Lösung

  1. Gehe den Text von links nach rechts durch und betrachte jedes Zeichen.

  2. Prüfe: Ist es ein Großbuchstabe (A bis Z) oder ein Kleinbuchstabe (a bis z)?

  3. Wenn es kein Buchstabe ist: übernimm das Zeichen unverändert.

  4. Wenn es ein Buchstabe ist: berechne die Position im Alphabet (Offset zum Basiszeichen A oder a).

  5. Addiere 13 und wickle mit mod 26 (damit du im Alphabet bleibst).

  6. Baue aus der neuen Position wieder ein Zeichen und hänge es an das Ergebnis.

 

Java-Implementierung (Variante A: straightforward)

public class Rot13Straightforward {
public static void main(String[] args) {
    String input = "Hello World!";
    String output = rot13(input);

    System.out.println("Input : " + input);
    System.out.println("Output: " + output);
    System.out.println("Back  : " + rot13(output));
}

public static String rot13(String text) {
    if (text == null) {
        return null;
    }

    StringBuilder result = new StringBuilder(text.length());

    for (int i = 0; i < text.length(); i++) {
        char c = text.charAt(i);

        if (c >= 'A' && c <= 'Z') {
            result.append(shiftUppercase(c));
        } else if (c >= 'a' && c <= 'z') {
            result.append(shiftLowercase(c));
        } else {
            result.append(c);
        }
    }

    return result.toString();
}

private static char shiftUppercase(char c) {
    int offset = c - 'A';
    int shifted = (offset + 13) % 26;
    return (char) ('A' + shifted);
}

private static char shiftLowercase(char c) {
    int offset = c - 'a';
    int shifted = (offset + 13) % 26;
    return (char) ('a' + shifted);
}

}

Diese Variante ist gut zum Verstehen: klare Bedingungen für Groß- und Kleinbuchstaben, alles andere bleibt stehen. Die Hilfsmethoden shiftUppercase und shiftLowercase halten die Schleife übersichtlich. Laufzeit ist linear: pro Zeichen genau eine kleine Rechnung.

 

Java-Implementierung (Variante B: Alternative oder Verbesserung)

import java.util.Objects; import java.util.function.IntUnaryOperator;

public class Rot13Reusable {

private static final int ROT = 13;

public static void main(String[] args) {
    String input = "Attack at dawn. 123";
    String output = rot13(input);

    System.out.println("Input : " + input);
    System.out.println("Output: " + output);
    System.out.println("Back  : " + rot13(output));
}

public static String rot13(String text) {
    return transform(text, Rot13Reusable::rot13Char);
}

public static String transform(String text, IntUnaryOperator mapper) {
    Objects.requireNonNull(mapper, "mapper must not be null");
    if (text == null) {
        return null;
    }

    StringBuilder result = new StringBuilder(text.length());
    for (int i = 0; i < text.length(); i++) {
        int mapped = mapper.applyAsInt(text.charAt(i));
        result.append((char) mapped);
    }
    return result.toString();
}

private static int rot13Char(int ch) {
    if (ch >= 'A' && ch <= 'Z') {
        return rotateWithinRange(ch, 'A', 'Z', ROT);
    }
    if (ch >= 'a' && ch <= 'z') {
        return rotateWithinRange(ch, 'a', 'z', ROT);
    }
    return ch;
}

private static int rotateWithinRange(int ch, int start, int end, int shift) {
    int alphabetSize = end - start + 1;
    int offset = ch - start;
    int rotated = (offset + shift) % alphabetSize;
    return start + rotated;
}

}

Hier trennst du die allgemeine Idee transform von der konkreten Regel rot13Char. Das ist wiederverwendbar: Du kannst später andere Mapper einsetzen (z.B. Großbuchstaben maskieren, Ziffern ersetzen, Caesar mit anderem Shift). Außerdem steckt die Alphabet-Logik in rotateWithinRange und ist nicht doppelt vorhanden.

 

Beispiel: Eingabe und Ausgabe

  • Eingabe Hello -> Ausgabe Uryyb
  • Eingabe Uryyb -> Ausgabe Hello
  • Eingabe Attack at dawn. 123 -> Ausgabe Nggnpx ng qnja. 123

 

Typische Fehler

  • Nur Großbuchstaben behandeln: Kleinbuchstaben werden vergessen und bleiben dann unverändert.
  • Falsches Wrapping: Nach Z muss es wieder bei A weitergehen (und nach z bei a).
  • Off-by-one bei der Alphabetgröße: Alphabet hat 26 Zeichen, nicht 25. Bei end - start + 1 fehlt das + 1 gern.
  • Sonderzeichen versehentlich verändern: Punkte, Leerzeichen oder Ziffern sollen exakt gleich bleiben.
  • String-Verkettung in der Schleife: result = result + c in einer Schleife ist unnötig langsam; nutze StringBuilder.
  • Null-Eingabe nicht bedacht: Entscheide bewusst, ob du null erlaubst und was dann passieren soll (hier: null rein, null raus).

 

Fazit

Mit ROT13 hast du eine einfache, aber sehr lehrreiche Transformation umgesetzt. Du hast gelernt, Zeichenbereiche sauber zu prüfen, mit Wrap-Around zu arbeiten und Ergebnisse effizient aufzubauen. In Variante B hast du außerdem gesehen, wie man die eigentliche Regel von einer generischen Transformationsfunktion trennt.