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'])) # FalseDie 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'])) # TrueAlternative: 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'])) # FalseAlle 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'])) # TrueSonderfä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'])) # FalseStrategie 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'])) # TrueKurzer 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
- House Robber: Rekurrenz aus Nehmen oder Überspringen
- Maximales Teilarray und Teilarray mit maximalem Produkt
- Word Break und String segmentieren
- Decode Ways und Pfade zählen