Sözcük Bölme ve Dizeyi Parçalara Ayırma
Bir dizenin sözlük sözcüklerine ayrılıp ayrılamayacağını belirlemek için tek boyutlu DP tablosu kullanın; O(n²) zamanı ve bir trie’ın bunu neden hızlandırdığını inceleyin.
Sözcük Bölme ve Dizeyi Parçalara Ayırma, CoddyKit'te ücretsiz bir Coding Interview Prep dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Coding Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Coding Interview Prep kursu toplamda 4 dersten oluşur.
Kelimeleri Ayırma Problemi
Kelimeleri Ayırma (LeetCode 139) şu soruyu sorar: Bir s dizesi ve bir sözcük sözlüğü verildiğinde, s dizgesinin sözlükteki bir veya daha fazla sözcükten oluşan, aralarında boşluk bulunan bir diziye ayrılıp ayrılamayacağını belirleyin. Örneğin s = 'leetcode' ve wordDict = ['leet', 'code'] için cevap True'dur; çünkü 'leet' + 'code' = 'leetcode'. Bu, klasik bir 1D DP problemidir.
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 Formülasyonu ve Durumu
dp[i] değerini, s[:i] alt dizesinin sözlük kullanılarak ayrılabilmesi durumunda True olacak şekilde tanımlayın. Temel durum dp[0] = True'dur (boş dize her zaman ayrılabilir). Her i konumu için j < i olan tüm konumları denetleyin: dp[j] doğruysa ve s[j:i] sözlükte bulunuyorsa dp[i] = True olur. Son cevap dp[len(s)] değeridir.
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'])) # FalseDP Tablosunu İzleme
s = 'leetcode' ve sözlük {'leet', 'code'} için: dp[0]=T. i=4 için: j=0, dp[0]=T ve s[0:4]='leet' sözlükte → dp[4]=T. i=8 için: j=4, dp[4]=T ve s[4:8]='code' sözlükte → dp[8]=T. Hiçbir sözcüğün sona ermediği diğer konumlar False olarak kalır. dp[8]=True cevabı, dizenin ayrılabildiğini doğrular.
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'])Zaman Karmaşıklığı Analizi
Naif DP, O(n²) zamanda çalışır: n dış yineleme ve her birinde en fazla n iç yineleme yapılır. Ancak s[j:i] dilimleme işlemi de O(n) maliyetli olduğundan Python'daki gerçek karmaşıklık O(n³)'tür. Bir iyileştirme, sözlükteki sözcükler üzerinde yineleme yapıp her sözcüğün i konumunda sona erip ermediğini denetlemektir; bu, W sözlük boyutu ve L ortalama sözcük uzunluğu olmak üzere O(n × W × L) verir. Çoğu mülakat girdisi için O(n²) veya O(n³) kabul edilebilir.
# 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Önbelleğe Alınmış Özyineleme Alternatifi
Aynı problem, yukarıdan aşağıya önbelleğe alma kullanılarak çözülebilir. can_break(start) adlı özyinelemeli bir işlev tanımlayın; bu işlev s[start:] dizgesinin ayrılabilir olması durumunda True döndürür. Her sözcüğü s[start:] dizgesinin ön eki olarak deneyin ve kalan kısım üzerinde özyinelemeli çağrı yapın. Aynı başlangıç indisini birden çok kez yeniden incelememek için sonuçları önbelleğe alın. Bu, aşağıdan yukarıya DP ile eşdeğerdir; ancak birçok konum erken elenirse uygulamada daha hızlı olabilir.
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'])) # FalseGeçerli Tüm Bölümlendirmeleri Döndürme
Kelimeleri Ayırma II (LeetCode 140) olası tüm bölümlendirmeleri ister. Yaklaşım, önbelleğe alma ile geri izlemedir: her konumdan özyinelemeli olarak ilerleyin ve bir sözcük eşleştiğinde kalan kısım için yineleyin. Tüm kısmi sonuçları dize listeleri olarak saklayın. TLE'den kaçınmak için her başlangıç indisinden oluşturulabilecek cümleler listesini önbelleğe alın. Cümle sayısı en kötü durumda üstel olabilir, ancak önbelleğe alma gereksiz hesaplamaları ortadan kaldırır.
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']Önek Ağacı Optimizasyonu
Sözlük büyük olduğunda veya sözcükler uzun olduğunda, tüm j değerleri için s[j:i] in word_set denetimi Python dize özetleme işlemi nedeniyle yavaştır. Bir önek ağacı, ağacı karakter karakter dolaşmanıza ve olanaksız yolları erkenden budamanıza olanak tanır. O(n) başlangıç konumunun tümünü denetlemek yerine yalnızca önek ağacında bulunan yolları izlersiniz. Geçerli sözcüklere götüren az sayıda önek olduğunda bu, pratik çalışma süresini önemli ölçüde azaltır.
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'])) # TrueSınır Durumları ve Kısıtlamalar
Önemli sınır durumları: (1) Boş dize: True döndürün (boş dize önemsiz biçimde bölümlendirilebilir). (2) Sözlükte bulunmayan sözcük: dp ilgili konumu hiçbir zaman True olarak ayarlamaz ve doğru biçimde False döndürür. (3) Örtüşen sözcükler: s='aaa' için sözlükte 'a' ve 'aa' bulunması gibi — DP, tüm j değerlerini denetleyerek bunu doğal biçimde ele alır. (4) Tekrarlanan karakterler: sözlük=['a','aa','aaa'] ve s='aaaaab' — yollar üstel sayıda olsa da önbelleğe alma bunları O(n²) ile sınırlar.
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)Dize Bölme Genelleştirmesi
Kelimeleri Ayırma, her türlü dize bölme problemine genellenebilir: Bir dize s belirli bir kurala göre bölümlendirilebilir mi? Sözlük aramasını herhangi bir O(1) veya O(L) denetimiyle değiştirin. Örneğin: s palindromlara bölümlendirilebilir mi? Bir sözcük kümesi yerine önceden hesaplanmış bir palindrom tablosu kullanın. DP yapısı aynıdır — yalnızca geçerlilik denetimi değişir.
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 ve BFS Yaklaşımı
Kelimeleri Ayırma, BFS en kısa yol problemi olarak da ifade edilebilir: Dizedeki her konum bir düğümdür ve s[j:i] sözlükte bulunuyorsa j'den i'ye bir kenar vardır. 0 numaralı düğümden başlayan BFS, n numaralı düğüme erişilip erişilemeyeceğini sorar. BFS, aynı O(n² × L) karmaşıklığını verir; ancak bir mülakat sırasında problemi bir çizge problemi olarak modelliyorsanız daha sezgisel olabilir.
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'])) # FalseMülakat İletişimi Stratejisi
Bir mülakatta şu düşünce sürecini adım adım açıklayın: (1) Her konumdaki seçimlerin daha önce erişilebilir olanlara bağlı olduğunu fark edin — bu, DP'yi işaret eder. (2) Durumu tanımlayın: dp[i] = s[:i] dizgesini bölebilir miyiz? (3) Kodlamadan önce bağıntıyı ve temel durumu belirtin. (4) Önce O(n²) çözümünü kodlayın, ardından devam sorusu olarak önek ağacı optimizasyonundan bahsedin. (5) Sınır durumlarını tartışın: boş dize, tek karakter, sözlükte bulunmayan sözcük.
# 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'])) # TrueKısa Kontrol
Bu dersteki Veri Yapıları ve Algoritmalar — Kodlama Mülakatına Hazırlık kavramlarını ne ölçüde anladığınızı sınayın.
Ders Özeti
Bu derste şunları öğrendiniz: dp[i], s[:i] dizgesinin sözlük sözcüklerine bölümlendirilebilir olup olmadığını gösterir, O(n²) bağıntısı, dp[j]=True olan ve s[j:i] değerinin sözcük kümesinde bulunduğu tüm bölme noktalarını j denetler ve bir önek ağacı, var olmayan önekleri erkenden budayarak iç döngüyü hızlandırabilir. Sırada, Fibonacci benzeri başka bir 1D DP örüntüsü olan Kod Çözme Yolları ve Yol Sayma konularını inceleyeceğiz.
Sıkça Sorulan Sorular
“Sözcük Bölme ve Dizeyi Parçalara Ayırma” dersi ücretsiz mi?
Evet — “Sözcük Bölme ve Dizeyi Parçalara Ayırma” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Coding Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Coding Interview Prep kursu toplamda 4 dersten oluşur.
“Sözcük Bölme ve Dizeyi Parçalara Ayırma” dersinde ne öğreneceğim?
Bir dizenin sözlük sözcüklerine ayrılıp ayrılamayacağını belirlemek için tek boyutlu DP tablosu kullanın; O(n²) zamanı ve bir trie’ın bunu neden hızlandırdığını inceleyin. Coding Interview Prep ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
Coding Interview Prep öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te Coding Interview Prep, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.
“Sözcük Bölme ve Dizeyi Parçalara Ayırma” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu Coding Interview Prep dersinde kod yazıp çalıştırabilir miyim?
Evet. Her Coding Interview Prep dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Ev Soyguncusu: Al veya Atla Bağıntısı
- Maksimum Alt Dizi ve Maksimum Çarpımlı Alt Dizi
- Sözcük Bölme ve Dizeyi Parçalara Ayırma
- Yolları Çözümleme ve Yolları Sayma