الگوریتم‌های تطبیق رشته: جستجوی ساده، Rabin-Karp، و فراتر از آن

جستجو برای یک الگو درون یک متن بزرگ‌تر یکی از رایج‌ترین عملیات‌ها در محاسبات است، از ویرایشگرهای متن تا تحلیل توالی DNA. این راهنمای جامع الگوریتم تطبیق رشته ساده و بدترین‌حالت درجه‌دومش را پوشش می‌دهد، سپس استفاده ماهرانه الگوریتم Rabin-Karp از هشینگ برای دستیابی به کارایی سریع حالت‌میانگین را توضیح می‌دهد، شامل نحوه مدیریت درست برخوردهای هش.

تطبیق رشتهالگوریتم Rabin-Karpجستجوی الگو

~4 دقیقه مطالعه · آخرین به‌روزرسانی ۱۶ شهریور ۱۴۰۵

مسئله تطبیق رشته

با داشتن یک Text (متن) T به طول n و یک Pattern (الگو) P به طول m (جایی که m ≤ nString-Matching Problem (مسئله تطبیق رشته) هر موقعیت در T که P به‌عنوان یک زیررشته رخ می‌دهد را می‌خواهد. این عملیات زیربنای توابع جستجوی ویرایشگر متن، تطبیق توالی DNA در بیوانفورماتیک، و تشخیص سرقت ادبی یا تکرار کد است.

الگوریتم تطبیق رشته ساده

ساده‌ترین رویکرد هر موقعیت شروع ممکن در متن را بررسی می‌کند، و الگو را کاراکتر‌به‌کاراکتر در هر موقعیت مقایسه می‌کند.

NAIVE-STRING-MATCHER(T, P, n, m):
  برای s = 0 تا n - m:
      اگر P[1..m] == T[s+1..s+m]:
          چاپ "الگو با جابه‌جایی رخ می‌دهد" s

در بدترین‌حالت، زمان اجرای این الگوریتم O((n-m+1)m) است، چون برای هرکدام از n-m+1 موقعیت شروع ممکن، تا m کاراکتر ممکن است نیاز به مقایسه داشته باشد پیش از پیداشدن یک عدم‌تطابق (یا تأیید یک تطبیق کامل).

یک بدترین‌حالت آسیب‌شناختی:
متن:    "aaaaaaaaaaaaaaaaaaaaaaaaaab"
الگو:   "aaaaaaaaab"

در تقریباً هر موقعیت شروع، الگوریتم باید
تقریباً همه m کاراکتر الگو را پیش از یافتن
عدم‌تطابق (یا تأیید تطبیق نهایی) مقایسه کند،
که رفتار بدترین‌حالت کامل O(nm) تولید می‌کند

با وجود این بدترین‌حالت ضعیف، الگوریتم ساده در عمل به‌طور معقولی برای متن معمولی خوب عمل می‌کند، جایی که عدم‌تطابق‌ها معمولاً در چند مقایسه کاراکتر اول پیدا می‌شوند، و سادگی‌اش آن را به یک پیش‌فرض معقول برای الگوهای کوتاه یا جستجوهای کم‌تکرار تبدیل می‌کند.

الگوریتم Rabin-Karp: استفاده از هشینگ برای سرعت

Rabin-Karp Algorithm (الگوریتم رابین-کارپ) رویکرد اساساً متفاوتی اتخاذ می‌کند: به‌جای مقایسه مستقیم کاراکترها در هر موقعیت، یک مقدار هش عددی، که پیش‌تر در این مجموعه درباره جداول هش بحث شد، برای الگو و برای هر زیررشته با طول-m متن محاسبه می‌کند، و فقط یک مقایسه کاراکتر کامل را وقتی مقادیر هش تطبیق دارند انجام می‌دهد.

محاسبه کارآمد مقادیر هش با هش غلتان

بینش کلیدی‌ای که این را کارآمد می‌کند یک تکنیک Rolling Hash (هش غلتان) است: به‌جای محاسبه مجدد هش هر زیررشته از صفر، که زمان O(m) به‌ازای هر موقعیت می‌گرفت و هر مزیت سرعتی را حذف می‌کرد، هش زیررشته بعدی می‌تواند در زمان O(1) از هش زیررشته فعلی محاسبه شود.

رفتار با هر زیررشته به‌عنوان یک عدد در مبنای d
(جایی که d اندازه الفبای کاراکتر است):

hash(T[s+1..s+m]) = T[s+1]·d^(m-1) + T[s+2]·d^(m-2)
                     + ... + T[s+m]

برای لغزاندن پنجره یک موقعیت به جلو:
hash(T[s+2..s+m+1]) =
  (hash(T[s+1..s+m]) - T[s+1]·d^(m-1)) · d + T[s+m+1]

این سهم کاراکتر پیشتاز را حذف می‌کند،
رقم‌های باقی‌مانده را شیفت می‌دهد، و
کاراکتر انتهایی جدید را اضافه می‌کند — همه در زمان O(1)

در عمل، این محاسبه به‌پیمانه یک عدد اول بزرگ q انجام می‌شود، تا مقادیر هش را در یک بازه قابل‌مدیریت نگه دارد و از سرریز اجتناب کند، در حالی که همچنان ویژگی اینکه زیررشته‌های برابر همیشه مقادیر هش برابر تولید می‌کنند را حفظ می‌کند.

الگوریتم

RABIN-KARP-MATCHER(T, P, n, m, d, q):
  h = d^(m-1) mod q
  p = 0    // مقدار هش الگو
  t0 = 0   // مقدار هش اولین پنجره متن
  برای i = 1 تا m:
      p = (d·p + P[i]) mod q
      t0 = (d·t0 + T[i]) mod q
  برای s = 0 تا n - m:
      اگر p == ts:
          اگر P[1..m] == T[s+1..s+m]:    // برای رد کردن تطبیق نادرست تأیید کن
              چاپ "الگو با جابه‌جایی رخ می‌دهد" s
      اگر s < n - m:
          ts+1 = (d·(ts - T[s+1]·h) + T[s+m+1]) mod q

چرا گام تأیید ضروری است

از آنجا که زیررشته‌های متفاوت گاهی می‌توانند همان مقدار هش را تولید کنند، رویدادی به نام Spurious Hit (تطبیق نادرست)، الگوریتم همیشه باید یک تطبیق کاراکتر-به-کاراکتر کامل را هروقت مقادیر هش توافق داشته باشند تأیید کند، به‌جای اعتماد صرف به تطبیق هش. رد‌کردن این گام

نوشته و پژوهش‌شده توسط دکتر شاهین صیامی

مقالات مرتبط

الگوریتم‌های نظریه اعداد: GCD، توان‌رسانی پیمانه‌ای، و RSA

رمزنگاری مدرن و بی‌شمار کاربرد الگوریتمی به تعداد کمی الگوریتم ظریف نظریه اعداد متکی است. این راهنمای جامع الگوریتم اقلیدس برای محاسبه بزرگ‌ترین مقسوم‌علیه مشترک، توان‌رسانی پیمانه‌ای سریع برای محاسبه کارآمد توان‌های بزرگ، و پایه ریاضی رمزنگاری RSA، یکی از پراستفاده‌ترین سیستم‌های رمزنگاری در جهان، را پوشش می‌دهد.

ادامه

اصول هندسه محاسباتی: جهت، تقاطع پاره‌خط، و پوسته محدب

الگوریتم‌های هندسی مسائل شامل نقاط، خطوط، و اشکال را حل می‌کنند، که در گرافیک کامپیوتری، برنامه‌ریزی مسیر رباتیک، و سیستم‌های اطلاعات جغرافیایی ظاهر می‌شوند. این راهنمای جامع آزمون جهت مبتنی بر ضرب خارجی که زیربنای تقریباً هر الگوریتم هندسی است، تشخیص تقاطع پاره‌خط ساخته‌شده روی آن آزمون، و الگوریتم اسکن گراهام برای محاسبه پوسته محدب یک مجموعه از نقاط را پوشش می‌دهد.

ادامه

الگوریتم‌های تقریبی: نزدیک‌شدن اثبات‌پذیر به بهینه برای مسائل سخت

وقتی یک مسئله NP-complete اثبات شود، یک راه‌حل کارآمد دقیق بعید است وجود داشته باشد، اما این به معنای رهاکردن کامل مسئله نیست. این راهنمای جامع الگوریتم‌های تقریبی را توضیح می‌دهد، که تضمین بهینه‌بودن را در ازای تضمین کارایی معامله می‌کنند، و مسائل پوشش رأس و فروشنده دوره‌گرد را به‌عنوان مثال‌های کلاسیک با نسبت‌های تقریبی اثبات‌پذیر پوشش می‌دهد.

ادامه

NP-Completeness توضیح داده شده: P، NP، و چرا برخی مسائل در برابر راه‌حل‌های کارآمد مقاومت می‌کنند

برخی مسائل برای دهه‌ها در برابر هر تلاشی برای یک الگوریتم کارآمد مقاومت کرده‌اند، با این حال هیچ‌کس اثبات نکرده یک راه‌حل کارآمد غیرممکن است. این راهنمای جامع کلاس‌های P و NP، مفهوم تقلیل‌های زمان-چندجمله‌ای مورد استفاده برای مقایسه سختی مسئله، و چگونگی اینکه اثبات NP-complete بودن یک مسئله شواهد قوی، هرچند نه اثبات، فراهم می‌کند که هیچ الگوریتم کارآمدی وجود ندارد را توضیح می‌دهد.

ادامه

جریان بیشینه: فورد-فالکرسون و قضیه برش-کمینه/جریان-بیشینه

مسائل جریان بیشینه بیشترین توان عملیاتی ممکن از میان یک شبکه با اتصالات محدود-به-ظرفیت را مدل‌سازی می‌کنند، از لوله‌های آب تا شبکه‌های داده. این راهنمای جامع شبکه‌های جریان را معرفی می‌کند، روش فورد-فالکرسون برای یافتن جریان بیشینه با استفاده از مسیرهای تقویتی را مرور می‌کند، و قضیه ظریف برش-کمینه/جریان-بیشینه که دو مسئله به‌ظاهر متفاوت را به یکی متصل می‌کند را توضیح می‌دهد.

ادامه

الگوریتم فلوید-وارشال: یافتن کوتاه‌ترین مسیرها بین هر جفت رأس

گاهی یک اپلیکیشن به کوتاه‌ترین فاصله بین هر جفت رأس ممکن نیاز دارد، نه فقط از یک منبع واحد. این راهنمای جامع مسئله کوتاه‌ترین مسیرهای همه‌جفت را توضیح می‌دهد، رابطه بازگشتی برنامه‌نویسی پویای ظریف پشت الگوریتم فلوید-وارشال را استخراج می‌کند، و کارایی آن را در برابر اجرای مکرر الگوریتم‌های منبع-واحد مقایسه می‌کند.

ادامه