0Pricing
Coding Interview Prep · Lektion

Word Break und String segmentieren

Verwenden Sie eine 1D-DP-Tabelle, um zu bestimmen, ob ein String in Wörterbuchwörter segmentiert werden kann, analysieren Sie die Laufzeit O(n²) und warum ein Trie sie beschleunigt.

Word Break und String segmentieren ist eine kostenlose Coding Interview Prep-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Coding Interview Prep-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Coding Interview Prep-Kurs umfasst insgesamt 4 Lektionen.

Das Word-Break-Problem

Word Break (LeetCode 139) stellt folgende Aufgabe: Bestimmen Sie für einen String s und ein Wörterbuch mit Wörtern, ob s in eine durch Leerzeichen getrennte Folge aus einem oder mehreren Wörtern des Wörterbuchs zerlegt werden kann. Für s = 'leetcode' und wordDict = ['leet', 'code'] lautet die Antwort beispielsweise True, weil 'leet' + 'code' = 'leetcode' gilt. Dies ist ein klassisches 1D-DP-Problem.

s = 'leetcode'
word_set = {'leet', 'code'}
# Can we split 'leetcode' into words from word_set?
# 'leet' in set → yes, 'code' in set → yes
# So: 'leetcode' = 'leet' + 'code' → True

s2 = 'catsandog'
word_set2 = {'cats', 'dog', 'sand', 'and', 'cat'}
# No matter how we split, last part 'og' not in dict
print('Expected: True, False')

DP-Formulierung und Zustand

Definieren Sie dp[i] als True, wenn der Teilstring s[:i] mithilfe des Wörterbuchs segmentiert werden kann. Der Basisfall ist dp[0] = True (der leere String kann immer segmentiert werden). Prüfen Sie für jede Position i alle Positionen j < i: Wenn dp[j] True ist und s[j:i] im Wörterbuch enthalten ist, gilt dp[i] = True. Die endgültige Antwort ist dp[len(s)].

def word_break(s, word_dict):
    word_set = set(word_dict)
    n = len(s)
    dp = [False] * (n + 1)
    dp[0] = True  # empty string
    
    for i in range(1, n + 1):
        for j in range(i):
            # If s[:j] is segmentable AND s[j:i] is a word
            if dp[j] and s[j:i] in word_set:
                dp[i] = True
                break  # no need to check other j values
    return dp[n]

print(word_break('leetcode', ['leet', 'code']))        # True
print(word_break('catsandog', ['cats','dog','sand','and','cat']))  # False

Die DP-Tabelle nachverfolgen

Für s = 'leetcode' und das Wörterbuch {'leet', 'code'} gilt: dp[0]=T. Bei i=4: j=0, dp[0]=T und s[0:4]='leet' ist im Wörterbuch enthalten → dp[4]=T. Bei i=8: j=4, dp[4]=T und s[4:8]='code' ist im Wörterbuch enthalten → dp[8]=T. Alle übrigen Positionen, an denen kein Wort endet, bleiben False. Die Antwort dp[8]=True bestätigt, dass der String segmentiert werden kann.

def word_break_trace(s, word_dict):
    word_set = set(word_dict)
    n = len(s)
    dp = [False] * (n + 1)
    dp[0] = True
    for i in range(1, n + 1):
        for j in range(i):
            if dp[j] and s[j:i] in word_set:
                dp[i] = True
                print(f'dp[{i}]=True via s[{j}:{i}]={repr(s[j:i])}')
                break
    print('dp table:', dp)
    return dp[n]

word_break_trace('leetcode', ['leet', 'code'])

Analyse der Zeitkomplexität

Die naive DP benötigt O(n²) Zeit: n äußere Durchläufe mal bis zu n innere Durchläufe. Das Ausschneiden von s[j:i] kostet jedoch ebenfalls O(n), sodass die tatsächliche Komplexität in Python O(n³) beträgt. Eine Optimierung besteht darin, über die Wörter im Wörterbuch zu iterieren und zu prüfen, ob jedes Wort an der Position i endet. Dadurch erhält man O(n × W × L), wobei W die Größe des Wörterbuchs und L die durchschnittliche Wortlänge bezeichnet. Für die meisten Eingaben in Interviews ist O(n²) oder O(n³) akzeptabel.

# Slightly faster: iterate over words rather than all j positions
def word_break_v2(s, word_dict):
    word_set = set(word_dict)
    n = len(s)
    dp = [False] * (n + 1)
    dp[0] = True
    for i in range(1, n + 1):
        for word in word_set:
            wl = len(word)
            # Does 'word' end exactly at position i?
            if i >= wl and dp[i - wl] and s[i - wl:i] == word:
                dp[i] = True
                break
    return dp[n]

print(word_break_v2('applepenapple', ['apple', 'pen']))  # True

Alternative: Rekursion mit Memoization

Dasselbe Problem kann mit Memoization auch Top-down gelöst werden. Definieren Sie eine rekursive Funktion can_break(start), die True zurückgibt, wenn s[start:] segmentiert werden kann. Probieren Sie jedes Wort als Präfix von s[start:] aus und wenden Sie die Rekursion auf den verbleibenden Teil an. Speichern Sie die Ergebnisse zwischen, um zu vermeiden, dass derselbe Startindex mehrfach untersucht wird. Dies entspricht der Bottom-up-DP, kann in der Praxis aber schneller sein, wenn viele Positionen frühzeitig ausgeschlossen werden.

from functools import lru_cache

def word_break_memo(s, word_dict):
    word_set = set(word_dict)
    
    @lru_cache(maxsize=None)
    def can_break(start):
        if start == len(s): return True
        for end in range(start + 1, len(s) + 1):
            if s[start:end] in word_set and can_break(end):
                return True
        return False
    
    return can_break(0)

print(word_break_memo('leetcode', ['leet', 'code']))  # True
print(word_break_memo('catsandog', ['cats','dog','sand','and','cat']))  # False

Alle gültigen Segmentierungen zurückgeben

Word Break II (LeetCode 140) verlangt alle möglichen Segmentierungen. Der Ansatz besteht aus Backtracking mit Memoization: Gehen Sie von jeder Position aus rekursiv vor und wenden Sie die Rekursion auf den verbleibenden Teil an, sobald ein Wort übereinstimmt. Speichern Sie alle Teilergebnisse als Listen von Strings. Um TLE zu vermeiden, speichern Sie die von jedem Startindex aus möglichen Sätze per Memoization. Die Anzahl der Sätze kann im ungünstigsten Fall exponentiell sein, aber Memoization beseitigt redundante Berechnungen.

from functools import lru_cache

def word_break_ii(s, word_dict):
    word_set = set(word_dict)
    
    @lru_cache(maxsize=None)
    def break_from(start):
        if start == len(s): return ['']
        results = []
        for end in range(start + 1, len(s) + 1):
            word = s[start:end]
            if word in word_set:
                for rest in break_from(end):
                    results.append(word if not rest else word + ' ' + rest)
        return results
    
    return break_from(0)

print(word_break_ii('catsanddog', ['cat','cats','and','sand','dog']))
# ['cat sand dog', 'cats and dog']

Trie-Optimierung

Wenn das Wörterbuch groß oder die Wörter lang sind, ist die Prüfung von s[j:i] in word_set für alle j aufgrund des Hashings von Python-Strings langsam. Ein Trie ermöglicht es Ihnen, den Trie Zeichen für Zeichen zu durchlaufen und unmögliche Pfade frühzeitig abzuschneiden. Statt alle O(n) Startpositionen zu prüfen, verfolgen Sie nur im Trie vorhandene Pfade. Dies verkürzt die Laufzeit in der Praxis erheblich, wenn nur wenige Präfixe zu gültigen Wörtern führen.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

def build_trie(words):
    root = TrieNode()
    for word in words:
        node = root
        for ch in word:
            node = node.children.setdefault(ch, TrieNode())
        node.is_end = True
    return root

def word_break_trie(s, word_dict):
    root = build_trie(word_dict)
    n = len(s)
    dp = [False] * (n + 1)
    dp[0] = True
    for i in range(n):
        if not dp[i]: continue
        node = root
        for j in range(i, n):
            ch = s[j]
            if ch not in node.children: break
            node = node.children[ch]
            if node.is_end:
                dp[j + 1] = True
    return dp[n]

print(word_break_trie('leetcode', ['leet', 'code']))  # True

Sonderfälle und Einschränkungen

Wichtige Sonderfälle: (1) Leerer String: True zurückgeben (der leere String kann trivial segmentiert werden). (2) Wort nicht im Wörterbuch: Die DP setzt die entsprechende Position nie auf True und gibt korrekt False zurück. (3) Überlappende Wörter: z. B. 'a' und 'aa' im Wörterbuch bei s='aaa' — die DP verarbeitet dies automatisch, indem sie alle j-Werte prüft. (4) Wiederholte Zeichen: s='aaaaab' mit dict=['a','aa','aaa'] — es gibt exponentiell viele Pfade, aber Memoization begrenzt ihre Anzahl auf O(n²).

def word_break(s, word_dict):
    word_set = set(word_dict)
    dp = [False] * (len(s) + 1)
    dp[0] = True
    for i in range(1, len(s) + 1):
        for j in range(i):
            if dp[j] and s[j:i] in word_set:
                dp[i] = True
                break
    return dp[len(s)]

# Edge cases
print(word_break('', ['hello']))          # True (empty string)
print(word_break('a', ['b']))             # False
print(word_break('aaa', ['a', 'aa']))     # True (many ways)

Verallgemeinerung der Zeichenkettensegmentierung

Word Break lässt sich auf jedes Problem der Zeichenkettensegmentierung verallgemeinern: Kann der String s nach einer bestimmten Regel partitioniert werden? Ersetzen Sie die Wörterbuchsuche durch eine Prüfung mit O(1) oder O(L). Beispiel: Kann s in Palindrome partitioniert werden? Verwenden Sie anstelle einer Wortmenge eine vorberechnete Palindromtabelle. Die DP-Struktur bleibt identisch — nur die Gültigkeitsprüfung ändert sich.

def palindrome_partition_possible(s):
    '''Can s be partitioned into palindromes? (Always yes — single chars are palindromes)'''
    n = len(s)
    # Precompute palindrome table
    is_pal = [[False]*n for _ in range(n)]
    for i in range(n): is_pal[i][i] = True
    for i in range(n-1): is_pal[i][i+1] = (s[i]==s[i+1])
    for length in range(3, n+1):
        for i in range(n-length+1):
            j = i + length - 1
            is_pal[i][j] = s[i]==s[j] and is_pal[i+1][j-1]
    # DP similar to word break
    dp = [False] * (n + 1)
    dp[0] = True
    for i in range(1, n + 1):
        for j in range(i):
            if dp[j] and is_pal[j][i-1]:
                dp[i] = True
                break
    return dp[n]

print(palindrome_partition_possible('aab'))  # True (a,a,b or aa,b)

DP- vs. BFS-Ansatz

Word Break kann auch als BFS-Kürzestpfadproblem formuliert werden: Jede Position im String ist ein Knoten, und es gibt eine Kante von j nach i, wenn s[j:i] im Wörterbuch enthalten ist. Eine BFS ab Knoten 0 prüft, ob Knoten n erreichbar ist. BFS hat dieselbe Komplexität von O(n² × L), kann aber intuitiver sein, wenn Sie das Problem im Interview als Graphenproblem modellieren.

from collections import deque

def word_break_bfs(s, word_dict):
    word_set = set(word_dict)
    n = len(s)
    visited = set()
    queue = deque([0])
    while queue:
        start = queue.popleft()
        if start == n: return True
        for end in range(start + 1, n + 1):
            if end not in visited and s[start:end] in word_set:
                visited.add(end)
                queue.append(end)
    return False

print(word_break_bfs('leetcode', ['leet', 'code']))    # True
print(word_break_bfs('catsandog', ['cats','dog','and','sand','cat']))  # False

Strategie für die Kommunikation im Interview

Gehen Sie im Interview den folgenden Denkprozess durch: (1) Erkennen Sie, dass die Entscheidungen an jeder Position davon abhängen, was zuvor erreichbar war — das deutet auf DP hin. (2) Definieren Sie den Zustand: dp[i] = Kann s[:i] segmentiert werden? (3) Nennen Sie die Rekurrenz und den Basisfall, bevor Sie programmieren. (4) Implementieren Sie zuerst die O(n²)-Lösung und erwähnen Sie anschließend die Trie-Optimierung als mögliche Erweiterung. (5) Besprechen Sie Sonderfälle: leerer String, einzelnes Zeichen, Wort nicht im Wörterbuch.

# Clean final solution to present in interview
def word_break(s, word_dict):
    '''O(n^2 * L) time, O(n + W) space where W = total word length in dict'''
    word_set = set(word_dict)   # O(W) space
    n = len(s)
    dp = [False] * (n + 1)     # O(n) space
    dp[0] = True
    for i in range(1, n + 1):
        for j in range(i):     # try all split points
            if dp[j] and s[j:i] in word_set:
                dp[i] = True
                break
    return dp[n]

# Time: O(n^2 * L) - n^2 pairs, each dict lookup is O(L)
# Space: O(n) for dp array, O(W) for word_set
print(word_break('applepenapple', ['apple', 'pen']))  # True

Kurzer Test

Testen Sie Ihr Verständnis der Konzepte aus Data Structures & Algorithms — Coding Interview Prep in dieser Lektion.

Zusammenfassung der Lektion

In dieser Lektion haben Sie Folgendes gelernt: dp[i] gibt an, ob s[:i] in Wörter des Wörterbuchs segmentiert werden kann, die O(n²)-Rekurrenz prüft alle Trennpositionen j, für die dp[j]=True gilt und s[j:i] in der Wortmenge enthalten ist und ein Trie die innere Schleife beschleunigen kann, indem nicht vorhandene Präfixe frühzeitig abgeschnitten werden. Als Nächstes untersuchen wir Decode Ways und Counting Paths, ein weiteres Fibonacci-ähnliches 1D-DP-Muster.

Häufig gestellte Fragen

Ist die Lektion „Word Break und String segmentieren“ kostenlos?

Ja — der vollständige Text von „Word Break und String segmentieren“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Coding Interview Prep-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Coding Interview Prep-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Word Break und String segmentieren“?

Verwenden Sie eine 1D-DP-Tabelle, um zu bestimmen, ob ein String in Wörterbuchwörter segmentiert werden kann, analysieren Sie die Laufzeit O(n²) und warum ein Trie sie beschleunigt. Du übst Coding Interview Prep mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Coding Interview Prep zu starten?

Keine Vorkenntnisse erforderlich. Coding Interview Prep auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Word Break und String segmentieren“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Coding Interview Prep-Lektion Code schreiben und ausführen?

Ja. Jede Coding Interview Prep-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. House Robber: Rekurrenz aus Nehmen oder Überspringen
  2. Maximales Teilarray und Teilarray mit maximalem Produkt
  3. Word Break und String segmentieren
  4. Decode Ways und Pfade zählen
← Zurück zu Coding Interview Prep