در علوم رایانه، **آرایه بلندترین پیشوند مشترک (آرایه ال‌سی‌پی)** (به انگلیسی: LCP array) یک داده‌ساختار کمکی برای آرایه پسوندی است. در این آرایه، طولانی‌ترین پیشوند مشترک بین هر دو پسوند متوالی موجود در آرایه پسوندی ذخیره می‌شود.

تاریخچه
-------

آرایه ال‌سی‌پی توسط Udi Manber و Gene Myers در سال ۱۹۹۰ به همراه آرایه پسوندی و برای بهبود سرعت الگوریتم جستجوی الگو در متن ارائه شد.

تعریف
-----

فرض کنیم A آرایه پسوندی رشته {\displaystyle S=S[1]S[2]...S[n]} بوده و {\displaystyle lcp(v,w)} نشان‌دهندهٔ طولانی‌ترین پیشوند مشترک رشته‌های v و w باشد. هم چنین زیررشتهٔ S از حرف i ام تا حرف j ام را با {\displaystyle S[i,j]} نشان می‌دهیم. در این صورت آرایه ال‌سی‌پی {\displaystyle H[1,n]} یک آرایه با اندازهٔ n از اعداد صحیح است که {\displaystyle H[1]} تعریف نشده‌است و {\displaystyle H[i]=lcp(S[A[i-1],n],S[A[i],n])} به ازای هر {\displaystyle 1<i\leq n}. با توجه به اینکه {\displaystyle A[i]} نشان‌دهندهٔ جایگاه شروع i امین کوچک‌ترین پسوند رشتهٔ S از نظر لغتنامه‌ای است، پس H[i] طول بلندترین پیشوند مشترک {\displaystyle i-1} امین و i امین پسوند رشتهٔ S از نظر لغتنامه‌ای است ({\displaystyle 1<i\leq n}).

مثال
----

رشتهٔ {\displaystyle S=banana\$} را در نظر بگیرید:

|  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- |
| i | ۱ | ۲ | ۳ | ۴ | ۵ | ۶ | ۷ |
| S[i] | b | a | n | a | n | a | $ |

و آرایهٔ پسوندی مرتب‌شدهٔ متناظر با آن:

|  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- |
| i | ۱ | ۲ | ۳ | ۴ | ۵ | ۶ | ۷ |
| A[i] | ۷ | ۶ | ۴ | ۲ | ۱ | ۵ | ۳ |

پسوندهای رشتهٔ S که به صورت ستونی و مرتب‌شده نوشته شده‌اند:

|  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- |
| i | ۱ | ۲ | ۳ | ۴ | ۵ | ۶ | ۷ |
| A[i] | ۷ | ۶ | ۴ | ۲ | ۱ | ۵ | ۳ |
| 1 | $ | a | a | a | b | n | n |
| 2 |  | $ | n | n | a | a | a |
| 3 |  |  | a | a | n | $ | n |
| 4 |  |  | $ | n | a |  | a |
| 5 |  |  |  | a | n |  | $ |
| 6 |  |  |  | $ | a |  |  |
| 7 |  |  |  |  | $ |  |  |

آرایهٔ ال‌سی‌پی با مقایسهٔ پسوندهای متوالی از نظر لغت‌نامه‌ای برای مشخص کردن بزرگ‌ترین پیشوند مشترک آن‌ها محاسبه می‌شود:

|  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- |
| i | ۱ | ۲ | ۳ | ۴ | ۵ | ۶ | ۷ |
| H[i] | ⊥ | ۰ | ۱ | ۳ | ۰ | ۰ | ۲ |

برای مثال، {\displaystyle H[4]=3} طول بزرگ‌ترین پیشوند مشترک بین {\displaystyle S[A[3],7]=S[4,7]=ana\$} و {\displaystyle S[A[4],7]=S[2,7]=anana\$} یعنی رشته {\displaystyle ana} است.

استفاده از آرایه ال‌سی‌پی برای جستجوی یک الگو در متن
----------------------------------------------------

فرض کنید می‌خواهیم تعداد رخدادهای یک الگوی P (به طول m) در یک متن T (به طول N) را بیابیم. می‌توان با دو بار استفاده از جستجوی دودویی، مکان اولین و آخرین رخداد P در آرایه پسوندی T را بدست آورد و در نتیجه تعداد رخدادهای رشته P در متن T بدست می‌آید. در هر کدام از جستجوهای دودویی {\displaystyle O(logN)} مقایسه نیاز است و با توجه به اینکه در هر مقایسه رشتهٔ P را با یکی از عناصر آرایه پسوندی مقایسه می‌کنیم، در بدترین حالت ممکن است نیاز به مقایسهٔ کامل دو رشته باشد که به معنی مقایسهٔ m کاراکتر است؛ بنابراین زمان اجرای این الگوریتم از {\displaystyle O(m\times logN)} است.

با استفاده از آرایه ال‌سی‌پی، الگوریتمی با زمان اجرای {\displaystyle O(m+logN)} ارائه می‌دهیم. به ازای هر {\displaystyle 1\leq i\leq n}، {\displaystyle S\_{i}} را برابر {\displaystyle S[A[i],n]} تعریف می‌کنیم؛ در واقع {\displaystyle S\_{i}} برابر iامین کوچک‌ترین پسوند S است. ابتدا فرض کنیم به ازای هر {\displaystyle 1\leq i,j\leq n} مقدار {\displaystyle lcp(S\_{i},S\_{j})} از قبل محاسبه شده‌است.
در هر مرحله از جستجوی دودویی، یک بازه {\displaystyle (L,...R)}در آرایه پسوندی به همراه نقطه میانی آن M را در نظر می‌گیریم. سپس با مقایسهٔ P و{\displaystyle S\_{M}} مشخص می‌کنیم که جستجو را در بازهٔ {\displaystyle (L,...M)} ادامه دهیم یا بازهٔ {\displaystyle (M,...R)}. فرض کنیم P بزرگتر از {\displaystyle S\_{M}} باشد و اولین حرفی از P و {\displaystyle S\_{M}} که متفاوت اند حرف {\displaystyle k+1} ام باشد؛ یعنی {\displaystyle lcp(P,S\_{M})=k}. پس در مرحلهٔ بعدی جستجو، بازه {\displaystyle (M,...R)} با نقطه میانی {\displaystyle M'} را در نظر می‌گیریم. با توجه به فرضی که کردیم، مقدار {\displaystyle lcp(S\_{M},S\_{M'})=k'} از قبل محاسبه شده‌است و در زمان O(1) قابل دسترس است. سه حالت ممکن است پیش بیاید:

* حالت ۱: {\displaystyle k<k'}، یعنی طولانی‌ترین پیشوند مشترک P و {\displaystyle S\_{M}} کمتر از طولانی‌ترین پیشوند مشترک {\displaystyle S\_{M}} و {\displaystyle S\_{M'}} است؛ بنابراین {\displaystyle k+1} امین حرف {\displaystyle S\_{M}} و {\displaystyle S\_{M'}} یکسان است و چون {\displaystyle P>S\_{M}} در نتیجه {\displaystyle P>S\_{M'}}. بنابراین جستجو را در بازهٔ {\displaystyle (M',...R)} ادامه می‌دهیم.
* حالت ۲: {\displaystyle k>k'}، یعنی P و {\displaystyle S\_{M}} پیشوند مشترک بیشتری نسبت به {\displaystyle S\_{M}} و {\displaystyle S\_{M'}} دارند. با توجه به اینکه آرایه پسوندی با ترتیب لغت‌نامه‌ای مرتب شده‌است، پس {\displaystyle S\_{M'}>S\_{M}}. از آنجا که اولین حرفی که {\displaystyle S\_{M}} و {\displaystyle S\_{M'}} در آن متفاوت اند حرف {\displaystyle k'+1} ام است و رشته‌های P و {\displaystyle S\_{M}} در این حرف یکسان اند، پس {\displaystyle S\_{M'}>P}و در نتیجه جستجو در بازه {\displaystyle (M,...M')} ادامه می‌یابد.
* حالت ۳: {\displaystyle k=k'}، یعنی رشته‌های {\displaystyle S\_{M}} و {\displaystyle S\_{M'}} در k حرف اول با رشتهٔ P یکسان اند. برای مقایسه رشته‌های {\displaystyle S\_{M'}} و P، این دو رشته را از حرف {\displaystyle k+1} ام به بعد مقایسه می‌کنیم تا به اولین حرف متفاوت برسیم. به این ترتیب در مرحلهٔ بعدی الگوریتم جستجوی دودویی، مقدار {\displaystyle lcp(P,S\_{M'})} را داریم و می‌توانیم به همین صورت الگوریتم جستجوی P را ادامه دهیم.

در الگوریتم بالا هر کاراکتر از رشتهٔ P حداکثر یک بار با کاراکترهای متن T مقایسه می‌شود؛ بنابراین زمان اجرای این الگوریتم {\displaystyle O(m+logN)} است.

اما فرض کرده بودیم به ازای هر {\displaystyle 1\leq i,j\leq n} مقدار {\displaystyle lcp(S\_{i},S\_{j})} از قبل محاسبه شده‌است. توجه کنیم که همهٔ بازه‌های ممکن {\displaystyle (L,...R)} در الگوریتم جستجوی دودویی ظاهر نمی‌شوند. به ازای هر نقطهٔ {\displaystyle 2\leq M\leq n-1}، دقیقاً یک بازه با نقطهٔ میانی M وجود دارد که در الگوریتم جستجوی دودویی ممکن است ظاهر شود. این بازه را با {\displaystyle (L\_{M},M,R\_{M})} نشان می‌دهیم و به ازای هر {\displaystyle 2\leq M\leq n-1} تعریف می‌کنیم:

* {\displaystyle Llcp[M]=lcp(S\_{L\_{M}},S\_{M})}
* {\displaystyle Rlcp[M]=lcp(S\_{M},S\_{R\_{M}})}

با توجه به الگوریتم گفته شده، فقط به مقادیر آرایه‌های {\displaystyle Llcp} و {\displaystyle Rlcp} نیاز داریم که حافظه‌ای از {\displaystyle O(N)} اشغال می‌کنند. هم‌چنین از روی آرایه ال‌سی‌پی و در زمان {\displaystyle O(N)} می‌توان مقادیر {\displaystyle Llcp} و {\displaystyle Rlcp} را محاسبه کرد.

ساختن آرایه ال‌سی‌پی
--------------------

در سال ۱۹۹۰ Manber و Myers الگوریتمی از {\displaystyle O(NlogN)} دادند که در کنار آرایه پسوندی، آرایه ال‌سی‌پی را نیز محاسبه می‌کند. در سال ۲۰۰۳ الگوریتمی از {\displaystyle O(N)} برای ساختن هم‌زمان آرایه پسوندی و آرایه ال‌سی‌پی ارایه شد.

در سال ۲۰۰۱ الگوریتمی ارایه شد که در {\displaystyle O(N)} آرایه ال‌سی‌پی را از روی متن و آرایه پسوندی محاسبه می‌کند.

جستارهای وابسته
---------------

* آرایه پسوندی
* الگوریتم جستجوی رشته

منابع
-----

1. 1 2 3  Manber, Udi; Myers, Gene (1990). *Suffix arrays: a new method for on-line string searches*. First Annual ACM-SIAM Symposium on Discrete Algorithms. pp. 319–327.
2. ↑  Kärkkäinen, Juha; Sanders, Peter (2003). *Simple linear work suffix array construction*. Proceedings of the 30th international conference on Automata, languages and programming. pp. 943–955. Retrieved 2012-08-28.
3. ↑  Kasai, T.; Lee, G.; Arimura, H.; Arikawa, S.; Park, K. (2001). *Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its Applications*. Proceedings of the 12th Annual Symposium on Combinatorial Pattern Matching. Lecture Notes in Computer Science. Vol. 2089. pp. 181–192. doi:10.1007/3-540-48194-X\_17. ISBN 978-3-540-42271-6.