অ্যাডভান্সড / স্পেশালপড়তে ৭ মিনিট লাগবে

সাফিক্স অ্যারে এবং এলসিপি অ্যারে (Suffix Array & LCP Array)

কোনো শব্দের সবগুলো সাফিক্স বা শেষাংশকে সাজিয়ে রাখা — স্ট্রিং খোঁজার দুনিয়ার এক জাদুকরী হাতিয়ার
build (naive): O(n² log n)build (prefix doubling): O(n log n)pattern search: O(m log n)space: O(n)

ধরুন একটা শব্দ হলো "জাম"। এর ৬টা সাফিক্স (suffix) বা শেষাংশ আছে: "জাম", "anana", "nana", "ana", "na", "a"। এখন এই অংশগুলোকে যদি ইংরেজি ডিকশনারির নিয়মে (alphabetically) ছোট থেকে বড় হিসেবে সাজান এবং মূল শব্দে তাদের শুরুর ইনডেক্সগুলো লিখে রাখেন, তবে ওই ইনডেক্সগুলোর লিস্টটাকেই বলা হয় সাফিক্স অ্যারে (Suffix Array)

"জাম"-এর জন্য সাজানেন লিস্টটা হবে এরকম:

  • 5 → "a"
  • 3 → "ana"
  • 1 → "anana"
  • 0 → "জাম"
  • 4 → "na"
  • 2 → "nana"

সাফিক্স অ্যারেটা দেখতে হবে শুধু শুরুর ইনডেক্সগুলোর একটা লিস্ট: [5, 3, 1, 0, 4, 2]। খেয়াল রাখবেন, এখানে সত্যিকারের শব্দগুলো (যেমন "ana", "nana") কিন্তু সেভ করা হয় না — দরকার পড়লে ইনডেক্স ধরে মূল শব্দ থেকে সেগুলো বের করে নেওয়া হয়। এর ফলেই মেমোরি খরচ একদম কম (মাত্র O(n)) থাকে।

Explore suffix array

← → অ্যারো কি (arrow key) ব্যবহার করুন · উপাদানগুলোতে ক্লিক করুন

অ্যারে বানানো: প্রিফিক্স ডাবলিং (Prefix doubling) — O(n log n)

সাধারণ বা বোকা (naive) পদ্ধতিতে সবগুলো সাফিক্সকে স্ট্রিং কম্পেয়ার করে সাজাতে গেলে O(n) সংখ্যক অক্ষর মেলাতে হয়, ফলে মোট সময় লাগে O(n² log n)। বড় শব্দের ক্ষেত্রে এটা মারাত্মক স্লো।

১৯৯০ সালে ম্যানবার ও মায়ার্স (Manber & Myers) একটা বুদ্ধি বের করেন, যার নাম প্রিফিক্স ডাবলিং (Prefix doubling) — এটা দিয়ে O(n log n) সময়েই কাজটা হয়ে যায়। এর মূল আইডিয়া হলো:

  1. প্রথমে সবগুলো সাফিক্সকে শুধু তাদের প্রথম ১টা অক্ষরের ওপর ভিত্তি করে র‍্যাঙ্ক (rank) বা সাজানেন হয়।
  2. এরপর প্রতি রাউন্ডে তুলনার দৈর্ঘ্য ডাবল বা দ্বিগুণ করা হয়: অর্থাৎ ২টা অক্ষর, এরপর ৪টা, তারপর ৮টা... এভাবে সাজানেন হয়।
  3. এভাবে মাত্র log₂(n) রাউন্ড চলার পরই সবগুলো সাফিক্স আলাদা করা হয়ে যায় এবং পুরো অ্যারে গোছানো কমপ্লিট হয়ে যায়।

প্রতি রাউন্ডে র‍্যাডিক্স সর্ট (radix sort) ব্যবহার করায় প্রতিটা রাউন্ডে মাত্র O(n) সময় লাগে। তাই মোট সময়: O(n log n) রাউন্ড × O(n) = O(n log n)।

এলসিপি অ্যারে (LCP Array — Longest Common Prefix)

সাফিক্স অ্যারের সাথেই আরেকটা জিনিস বানানো হয়, যার নাম এলসিপি অ্যারে (LCP array)lcp[i] মানে হলো: সাজানেন লিস্টে 'i' নম্বর সাফিক্স এবং তার ঠিক আগের '(i-1)' নম্বর সাফিক্সের মধ্যে প্রথম দিককার ঠিক কয়টা অক্ষর হুবহু সেম বা কমন আছে।

"জাম"-এর জন্য LCP হলো: [-, 1, 3, 0, 0, 2] (০ নম্বর পজিশনের আগে কেউ নেই; "a" আর "ana"-এর মধ্যে প্রথম ১টি অক্ষর কমন; "ana" আর "anana"-এর মধ্যে প্রথম ৩টি অক্ষর কমন ইত্যাদি)।

কোথায় কাজে লাগে এই সাফিক্স + এলসিপি?

  • প্যাটার্ন খোঁজা (Pattern search): সাফিক্স অ্যারের ওপর বাইনারি সার্চ চালিয়ে খুব সহজেই যেকোনো প্যাটার্ন খুঁজে বের করা যায়। সময় লাগে O(m log n), যেখানে m হলো প্যাটার্নের দৈর্ঘ্য।
  • সবচেয়ে বড় রিপিট হওয়া শব্দ (Longest repeated substring): এলসিপি অ্যারের মধ্যে সবচেয়ে বড় যে সংখ্যাটা, সেটাই হলো মূল শব্দের ভেতর সবচেয়ে বড় রিপিট হওয়া অংশ।
  • কয়টা আলাদা সাবস্ট্রিং আছে (Distinct substrings): সাবস্ট্রিংয়ের মোট সম্ভাব্য সংখ্যা n(n+1)/2 থেকে এলসিপি অ্যারের সব সংখ্যার যোগফল বাদ দিলেই উত্তর পাওয়া যায়।
দ্রষ্টব্য: সাফিক্স ট্রি (Suffix tree) দিয়েও এই সব কাজ করা যায়, কিন্তু সাফিক্স অ্যারে মেমোরি অনেক কম খায়। কারণ ট্রিতে অনেকগুলো নোড (node) আর এজ (edge) বানাতে হয়, যেখানে সাফিক্স অ্যারে জাস্ট n-সংখ্যক ইনটিজার (integers) বা ইনডেক্স জমা রাখে।

Complexity

অ্যারে বানানো (প্রিফিক্স ডাবলিং)
log n বার ডাবলিং রাউন্ড চলে, আর প্রতিবার র‍্যাডিক্স সর্ট দিয়ে O(n) সময় লাগে
খুব ফাস্ট O(n log n)
প্যাটার্ন খোঁজা
সাফিক্স অ্যারের ওপর বাইনারি সার্চ; m = খোঁজা প্যাটার্নটার দৈর্ঘ্য
খুব ফাস্ট O(m log n)
সবচেয়ে লম্বা রিপিট হওয়া শব্দ
এলসিপি (LCP) অ্যারের সবচেয়ে বড় মানটাই হলো উত্তর
লিনিয়ার (বানানোর পর) O(n)
জায়গা (Space)
এখানে শুধু শুরুর ইনডেক্সগুলো সেভ করা হয়, পুরো শব্দ বা সাফিক্স পুনরায় সেভ করা হয় না
লিনিয়ার O(n)
Challenge

ছোট কুইজ

'জাম'-এর সাফিক্স অ্যারে হলো [5,3,1,0,4,2]। এখানে ১ নম্বর ইনডেক্সে থাকা ভ্যালু 3 দিয়ে কী বোঝায়?

পড়া চালিয়ে যান

ট্রাই (Trie বা Prefix Tree)
শব্দের প্রথম কয়েকটা অক্ষর দিয়ে পুরো শব্দ খোঁজা — চোখের পলকে অটোকারেক্ট বা সাজেশন
বাইনারি ট্রি (Binary Trees)
এমন একটা ফ্যামিলি ট্রি, যেখানে বাবা-মায়ের সর্বোচ্চ দুজন করেই সন্তান থাকতে পারে
স্পার্স টেবিল (Sparse Table)
একবার O(n log n) সময় খরচ করে টেবিল সাজিয়ে নিলে, যেকোনো রেঞ্জের সবচেয়ে ছোট সংখ্যা বের করা যায় চোখের পলকে — O(1) স্পিডে