الگوریتم KMP (کنوت–موریس–پرات)
تطبیق رشته در O(n + m): هیچ نویسهای از متن دوباره خوانده نمیشود؛ بعد از ناهمخوانی، تابع پیشوند میگوید الگو چقدر جلو برود.
روش ساده بعد از ناهمخوانی الگو را فقط یک خانه جلو میبرد و دوباره از اول مقایسه میکند. ولی ما از قبل میدانیم q نویسهی آخر متن با P[0..q−1] برابر بودهاند. پس:
- زمان:
O(n + m) - حافظه:
O(m) - CLRS، بخش ۳۲.۴
- هوروویتز–ساهنی، فصل ۲
الگوی ababa در bacbababababa سه بار پیدا میشود، در جابهجاییهای ۴، ۶ و ۸؛ بعد از رخداد اول، KMP همپوشانی را نگه میدارد و از اول شروع نمیکند.
def kmp_match(T, P):n, m = len(T), len(P)pi = prefix_function(P)matches = []q = 0 # how many characters of P match the text ending hereshift = 0 # where P sits under T, for the picturefor i in range(n):while q > 0 and P[q] != T[i]:q = pi[q - 1]if P[q] == T[i]:q = q + 1if q == m:matches.append(i - m + 1)q = pi[q - 1]shift = i - q + 1return matchesdef prefix_function(P):pi = [0] * len(P)k = 0for q in range(1, len(P)):while k > 0 and P[k] != P[q]:k = pi[k - 1]if P[k] == P[q]:k = k + 1pi[q] = kreturn pi
شروع اجرا
0123456789101112
bacbababababa
ababa
pi—
matches—
متغیرهای محلی
T'bacbababababa'
P'ababa'
نارنجی = در همین گام عوض شد
Call stack
- kmp_match()خط 1
گام ۱ از ۱۱۵
توضیح
ایده
روش ساده بعد از ناهمخوانی الگو را فقط یک خانه جلو میبرد و دوباره از اول مقایسه میکند. ولی ما از قبل میدانیم q نویسهی آخر متن با P[0..q−1] برابر بودهاند. پس:
- اگر
T[i]باP[q]بخواند،qیکی زیاد میشود. - اگر نخواند،
qرا بهπ[q − 1](بلندترین مرزِ قسمت جورشده) برمیگردانیم و دوباره امتحان میکنیم؛ متن دوباره خوانده نمیشود. - اگر
q = mشد، الگو پیدا شده؛ باq = π[m − 1]ادامه میدهیم تا رخدادهای همپوشان هم پیدا شوند.
هزینه
i فقط جلو میرود و هر برگشت q را کم میکند؛ پس کل کار O(n) بهاضافهی O(m) برای ساختن π.
در trace به چه چیزی نگاه کنی
- اشارهگر
iروی متن فقط جلو میرود؛ الگو زیر متن با جابهجایی فعلی کشیده شده و خانههای پررنگش جور شدهاند.