0Pricing
DSA Interview Prep · Ders

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 DSA 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, DSA Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. DSA 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']))  # False

DP 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']))  # False

Geç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']))  # True

Sı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']))  # False

Mü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']))  # True

Kı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 DSA Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. DSA 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. DSA 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.

DSA Interview Prep öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te DSA 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 DSA Interview Prep dersinde kod yazıp çalıştırabilir miyim?

Evet. Her DSA 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

  1. Ev Soyguncusu: Al veya Atla Bağıntısı
  2. Maksimum Alt Dizi ve Maksimum Çarpımlı Alt Dizi
  3. Sözcük Bölme ve Dizeyi Parçalara Ayırma
  4. Yolları Çözümleme ve Yolları Sayma
← DSA Interview Prep Sayfasına Dön