MINT lernen

Abituraufgaben: Rekursion auf Zeichenketten

Zwei Aufgaben im Abiturformat — von der Geheimschrift bis zum Klammer-Check im Formeleditor.

Dein Fortschritt:
0 / 0 Aufgaben
1

Geheimschrift

AFB I–II

Für eine Schnitzeljagd werden Botschaften aus Großbuchstaben verschlüsselt, indem jeder Buchstabe um k Stellen im Alphabet verschoben wird; nach Z geht es mit A weiter. Die abgebildete Methode verwendet dazu die Zeichencodes aus Kapitel 1.

Material: Methode verschiebe
static String verschiebe(String s, int k) {
    if (s.length() == 0) {
        return "";
    }
    char c = s.charAt(0);
    char neu = (char) ('A' + (c - 'A' + k) % 26);
    return neu + verschiebe(s.substring(1), k);
}
  1. Beschreiben Sie, wie die Methode eine Zeichenkette rekursiv verarbeitet, und erläutern Sie die Zeile, in der neu berechnet wird. 4 BE
  2. Wenden Sie die Methode auf verschiebe("ZEBRA", 3) an. Geben Sie die Aufrufe und das Ergebnis an. 4 BE
  3. Erweitern Sie die Methode so, dass Leerzeichen unverändert übernommen werden. 4 BE
  4. Schätzen Sie ab, wie viele Zeichen durch substring insgesamt kopiert werden, wenn eine Botschaft mit 1000 Buchstaben verschlüsselt wird, und nennen Sie eine Möglichkeit, das zu vermeiden. 3 BE

Insgesamt 15 BE

Hinweise

Hinweis zu Aufgabe a)
Unterscheiden Sie Abbruchbedingung, Bearbeitung des ersten Zeichens und Selbstaufruf. Rechnen Sie die Zeile für c = 'Z' und k = 3 aus.
Hinweis zu Aufgabe b)
Jeder Aufruf erzeugt genau einen Buchstaben des Ergebnisses.
Hinweis zu Aufgabe c)
Das Leerzeichen hat keinen Platz im Alphabet — es braucht einen eigenen Fall.
Hinweis zu Aufgabe d)
Der erste Rest hat 999 Zeichen, der nächste 998 …

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

Ist die Zeichenkette leer, wird "" zurückgegeben. Sonst wird das erste Zeichen verschlüsselt und vor die rekursiv verschlüsselte Restzeichenkette gesetzt. c - 'A' ist die Position des Buchstabens im Alphabet (0 bis 25), dazu kommt k; % 26 sorgt dafür, dass nach Z wieder A kommt. 'A' + … liefert den Zeichencode des neuen Buchstabens, die Typumwandlung (char) macht daraus ein Zeichen. Beispiel: Z hat Position 25, (25 + 3) % 26 = 2, also C.

Erwartungshorizont zu Aufgabe b)

Aufrufe: verschiebe("ZEBRA", 3), verschiebe("EBRA", 3), verschiebe("BRA", 3), verschiebe("RA", 3), verschiebe("A", 3), verschiebe("", 3). Rückgaben von hinten: "", "D", "UD", "EUD", "HEUD", "CHEUD".

Erwartungshorizont zu Aufgabe c)
static String verschiebe(String s, int k) {
    if (s.length() == 0) {
        return "";
    }
    char c = s.charAt(0);
    String rest = verschiebe(s.substring(1), k);
    if (c == ' ') {
        return " " + rest;
    }
    char neu = (char) ('A' + (c - 'A' + k) % 26);
    return neu + rest;
}

Test: verschiebe("ZEBRA ZOO", 3) liefert "CHEUD CRR". Das Leerzeichen muss vor der Berechnung abgefangen werden, sonst entstünde ein falsches Zeichen.

Erwartungshorizont zu Aufgabe d)

Die Reste haben 999, 998, …, 1, 0 Zeichen; zusammen \(\frac{999 \cdot 1000}{2} \approx 500\,000\) kopierte Zeichen, der Aufwand wächst quadratisch mit der Länge. Abhilfe: statt substring einen Indexparameter i übergeben und s.charAt(i) verwenden (oder iterativ arbeiten); dann wird jedes Zeichen nur einmal gelesen.

2

Klammern im Formeleditor

AFB II–III

Ein Formeleditor prüft, ob in einer Eingabe jede öffnende runde Klammer genau einmal geschlossen wird. Andere Zeichen werden übergangen. Die Methode wird mit ausgeglichen(s, 0) aufgerufen.

Material: Methode ausgeglichen
static boolean ausgeglichen(String s, int offen) {
    if (offen < 0) {
        return false;
    }
    if (s.length() == 0) {
        return offen == 0;
    }
    char c = s.charAt(0);
    if (c == '(') {
        return ausgeglichen(s.substring(1), offen + 1);
    }
    if (c == ')') {
        return ausgeglichen(s.substring(1), offen - 1);
    }
    return ausgeglichen(s.substring(1), offen);
}
  1. Analysieren Sie den Aufruf ausgeglichen("(a)(b", 0) mit einer Tabelle der Aufrufe und Parameterwerte. 4 BE
  2. Erläutern Sie die Bedeutung des Parameters offen und die Notwendigkeit der Abbruchbedingung offen < 0 am Beispiel ")(". 4 BE
  3. Entwerfen Sie eine rekursive Methode tiefe(String s, int offen, int max), die für eine ausgeglichene Eingabe die größte Verschachtelungstiefe liefert, z. B. 3 für "(()(()))". Gestartet wird mit tiefe(s, 0, 0). 5 BE
  4. Beurteilen Sie den Vorschlag, den Parameter offen durch eine globale Variable zu ersetzen. 3 BE

Insgesamt 16 BE

Hinweise

Hinweis zu Aufgabe a)
Notieren Sie für jeden Aufruf den Rest s und den Wert von offen.
Hinweis zu Aufgabe b)
Was zählt offen? Was bedeutet ein negativer Wert?
Hinweis zu Aufgabe c)
Führen Sie zusätzlich zum Zähler den bisher größten Wert als Parameter mit.
Hinweis zu Aufgabe d)
Denken Sie an lokale und globale Variablen aus Kapitel 1: Was passiert beim zweiten Aufruf der Methode?

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)
AufrufsoffenAktion
1"(a)(b"0( → offen + 1
2"a)(b"1anderes Zeichen
3")(b"1) → offen − 1
4"(b"0( → offen + 1
5"b"1anderes Zeichen
6""1leer: offen == 0 ist falsch

Rückgabe false, durchgereicht bis zum ersten Aufruf: Eine Klammer bleibt offen.

Erwartungshorizont zu Aufgabe b)

offen zählt die bisher geöffneten, noch nicht geschlossenen Klammern. Ein negativer Wert heißt: Es wurde eine Klammer geschlossen, die nie geöffnet war. Bei ")(" wird offen nach dem ersten Zeichen −1. Ohne die Prüfung ginge es weiter, das ( brächte den Zähler wieder auf 0 und die Methode meldete fälschlich true. Mit der Prüfung liefert sie sofort false.

Erwartungshorizont zu Aufgabe c)
static int tiefe(String s, int offen, int max) {
    if (s.length() == 0) {
        return max;
    }
    char c = s.charAt(0);
    if (c == '(') {
        offen++;
        if (offen > max) {
            max = offen;
        }
    } else if (c == ')') {
        offen--;
    }
    return tiefe(s.substring(1), offen, max);
}

tiefe("(()(()))", 0, 0) liefert 3. max wird wie offen als Parameter weitergereicht, sodass jeder Aufruf den bisher größten Wert kennt.

Erwartungshorizont zu Aufgabe d)

Der Vorschlag ist ungünstig. Eine globale Variable müsste vor jedem neuen Aufruf von außen auf 0 zurückgesetzt werden; wer das vergisst, erhält falsche Ergebnisse, sobald die Methode ein zweites Mal verwendet wird. Außerdem wäre die Methode nicht mehr allein aus ihren Parametern verständlich. Als Parameter gehört der Zählerstand zu jedem Aufruf und kann nicht versehentlich von anderen Programmteilen verändert werden. Der Speicherbedarf ist in beiden Fällen ähnlich.