string - শেকড় অর্থ




প্লেইন ইংরাজিতে ইউকোননের প্রত্যয় গাছের অ্যালগরিদম (4)

আমি জোগোজপানের উত্তর প্রদত্ত দৃষ্টিভঙ্গির সাথে যথোপযুক্ত সৃষ্টিকর্তা বাস্তবায়ন করার চেষ্টা করেছি, কিন্তু নিয়মগুলির জন্য শব্দ ব্যবহার করার কারণে এটি কিছু ক্ষেত্রে কাজ করে নি। তাছাড়া, আমি উল্লেখ করেছি যে কেউ এই পদ্ধতি ব্যবহার করে একেবারে সঠিক প্রতিক্রিয়া গাছ বাস্তবায়নে পরিচালিত হয়নি। নীচে আমি জোগোজাপানের উত্তরের একটি "ওভারভিউ" বিধিগুলির কিছু পরিবর্তন নিয়ে লিখব। আমরা গুরুত্বপূর্ণ প্রতিক্রিয়া লিঙ্ক তৈরি করতে ভুলে যখন ক্ষেত্রে বর্ণনা করব।

ব্যবহৃত অতিরিক্ত ভেরিয়েবল

  1. সক্রিয় বিন্দু - একটি ট্রিপল (সক্রিয়_ নোড; সক্রিয়_অ্যাজ; সক্রিয়_লম্বন), যেখানে আমরা একটি নতুন প্রতিক্রিয়া সন্নিবেশ করা শুরু করতে হবে তা দেখানো হচ্ছে।
  2. বাকি - প্রত্যয় সংখ্যার আমরা আমাদের অবশ্যই যোগ করতে হবে। উদাহরণস্বরূপ, যদি আমাদের শব্দ 'abcaabca' হয় এবং অবশিষ্ট = 3, এর অর্থ হল আমাদের অবশ্যই 3 টি শেষ প্রতিক্রিয়া প্রক্রিয়া করতে হবে: bca , ca এবং a

আসুন একটি অভ্যন্তরীণ নোডের ধারণা ব্যবহার করি - সমস্ত নোড ছাড়া, রুট এবং পাতাগুলি অভ্যন্তরীণ নোড ছাড়া

পর্যবেক্ষণ 1

যখন আমাদের চূড়ান্ত প্রতিক্রিয়াটি আমাদের সন্নিবেশ করা দরকার তা ইতিমধ্যেই গাছের মধ্যে বিদ্যমান রয়েছে, গাছটি নিজেই পরিবর্তিত হয় না (আমরা শুধুমাত্র active point এবং remainder আপডেট করি)।

পর্যবেক্ষণ 2

যদি কোনও সময়ে active_length বর্তমান প্রান্ত ( edge_length ) এর দৈর্ঘ্যের সমান বা সমান হয়, তবে আমরা active point edge_length চেয়ে active_length বেশি না হওয়া পর্যন্ত আমাদের active point নীচে active_length

এখন, এর নিয়মগুলি পুনরায় সংজ্ঞায়িত করা যাক:

নিয়ম 1

সক্রিয় নোড = রুট থেকে একটি সন্নিবেশের পরে, সক্রিয় দৈর্ঘ্য 0 এর চেয়ে বড়, তারপর:

  1. সক্রিয় নোড পরিবর্তন করা হয় না
  2. সক্রিয় দৈর্ঘ্য হ্রাস করা হয়
  3. সক্রিয় প্রান্তটি সরানো হয় (পরবর্তী প্রিক্সের প্রথম চরিত্রটিতে আমরা প্রবেশ করতে হবে)

নিয়ম 2

যদি আমরা একটি নতুন অভ্যন্তরীণ নোড তৈরি করি অথবা একটি অভ্যন্তরীণ নোড থেকে কোনও অভ্যন্তরীণ নোড তৈরি করি , এবং এটি বর্তমান পদক্ষেপের মতো প্রথম অভ্যন্তরীণ নোড নয় , তবে আমরা পূর্বের অনুরূপ নোডটি একটি প্রিক্স লিঙ্কের মাধ্যমে লিঙ্ক করি

Rule 2 এর এই সংজ্ঞাটি জোগোজপানের থেকে আলাদা ', যেমন আমরা কেবলমাত্র নতুন তৈরি অভ্যন্তরীণ নোড নয়, অভ্যন্তরীণ নোডগুলিও বিবেচনা করি, যার থেকে আমরা একটি সন্নিবেশ তৈরি করি।

নিয়ম 3

সক্রিয় নোডের একটি সন্নিবেশের পরে যা রুট নোড নয়, আমাদের অবশ্যই প্রিক্স লিঙ্কটি অনুসরণ করতে হবে এবং সক্রিয় নোডটিকে নোডের দিকে নির্দেশ করবে। যদি কোনও প্রতিক্রিয়া লিঙ্ক না থাকে তবে রুট নোডের সক্রিয় নোড সেট করুন। কোন ভাবেই, সক্রিয় প্রান্ত এবং সক্রিয় দৈর্ঘ্য অপরিবর্তিত থাকুন।

Rule 3 এর এই সংজ্ঞাতে আমরা পাতা নোডের প্রবেশগুলি (শুধুমাত্র বিভক্ত নোডগুলি নয়) বিবেচনা করি।

এবং অবশেষে, পর্যবেক্ষণ 3:

যখন আমরা গাছটিতে যোগ করতে চাই, ইতিমধ্যে প্রান্তে, আমরা Observation 1 অনুসারে, শুধুমাত্র active point এবং remainder আপডেট করি, গাছটি অপরিবর্তিত রেখে চলে। কিন্তু যদি কোনও অভ্যন্তরীণ নোড প্রতিক্রিয়া লিঙ্কের প্রয়োজন হিসাবে চিহ্নিত থাকে, তবে আমাদের একটি প্রক্সিক লিঙ্কের মাধ্যমে আমাদের বর্তমান active node সাথে সেই নোডটিকে অবশ্যই সংযুক্ত করতে হবে।

আসুন আমরা cdddcdc এর জন্য একটি প্রতিক্রিয়া গাছের উদাহরণটি দেখি যদি আমরা এই ক্ষেত্রে একটি প্রতিক্রিয়া লিঙ্ক যুক্ত করি এবং আমরা না করি তবে:

  1. যদি আমরা একটি প্রতিক্রিয়া লিঙ্কের মাধ্যমে নোডগুলিকে সংযুক্ত করি না:

    • শেষ চিঠি যোগ করার আগে c :

    • শেষ চিঠি যোগ করার পর c :

  2. যদি আমরা একটি প্রতিক্রিয়া লিঙ্কের মাধ্যমে নোডগুলিকে সংযুক্ত করি:

    • শেষ চিঠি যোগ করার আগে c :

    • শেষ চিঠি যোগ করার পর c :

মনে হচ্ছে কোন উল্লেখযোগ্য পার্থক্য নেই: দ্বিতীয় ক্ষেত্রে আরও দুইটি প্রতিক্রিয়া লিঙ্ক রয়েছে। কিন্তু এই প্রতিক্রিয়া লিঙ্ক সঠিক , এবং তাদের মধ্যে একটি - নীল নোড থেকে লাল এক - সক্রিয় বিন্দু সঙ্গে আমাদের পদ্ধতির জন্য খুবই গুরুত্বপূর্ণ । সমস্যাটি যদি আমরা এখানে কোন প্রতিক্রিয়া লিঙ্ক না রাখি, পরে, যখন আমরা গাছটিতে কিছু নতুন অক্ষর যোগ করি, Rule 3 কারণে গাছটিতে কিছু নোড যোগ করা বাদ দিতে পারি, কারণ এর অনুসারে যদি কোন প্রত্যয় লিঙ্ক নেই, তাহলে আমাদের active_node সক্রিয় active_node করা আবশ্যক।

আমরা যখন গাছের শেষ চিঠি যোগ করছিলাম, তখন লাল নডটি নীল নোডের (সন্নিহিত 'c' ) সন্নিবেশ তৈরি করার আগেই বিদ্যমান ছিল । নীল নোড থেকে একটি সন্নিবেশ ছিল, আমরা এটি একটি প্রতিক্রিয়া লিঙ্ক প্রয়োজন হিসাবে চিহ্নিত। তারপর, সক্রিয় পয়েন্ট পদ্ধতির উপর নির্ভর করে, active node লাল নোড সেট করা হয়েছিল। কিন্তু আমরা লাল নোড থেকে একটি সন্নিবেশ করিনি, যেমন অক্ষর 'সি' ইতিমধ্যে প্রান্তে রয়েছে। এর অর্থ কি নীল নোড অবশ্যই একটি প্রতিক্রিয়া লিঙ্ক ছাড়া বাকি থাকতে হবে? না, আমরা একটি প্রিক্স লিঙ্কের মাধ্যমে লাল একটিকে নীল নোডকে অবশ্যই সংযুক্ত করতে হবে। কেন এটা সঠিক? কারণ সক্রিয় বিন্দু পদ্ধতির নিশ্চয়তা দেয় যে আমরা একটি সঠিক স্থানে পৌঁছাই, অর্থাৎ, পরবর্তী স্থানে যেখানে আমাদের একটি ছোট্ট प्रत्यত্তের সন্নিবেশ প্রক্রিয়া করতে হবে।

অবশেষে, এখানে আমার প্রতিক্রিয়া গাছের বাস্তবায়ন রয়েছে:

  1. Java
  2. C++

আশা করি যে এই "ওভারভিউ" জোগোজাপানের বিস্তারিত উত্তর দিয়ে মিলিত হবে কেউ তার নিজের স্বপিফ গাছ প্রয়োগ করতে সহায়তা করবে।

আমি এই সময়ে একটু পুরু মনে। আমি দিনগুলো পেরিয়ে আসার জন্য যথোপযুক্ত সৃষ্টিকর্তা গাছের চারপাশে আমার মাথা মোড়ানো চেষ্টা করছি, কিন্তু আমার গাণিতিক ব্যাকগ্রাউন্ড নেই, কারণ ব্যাখ্যাগুলির মধ্যে অনেকগুলি গাণিতিক প্রতীকবিজ্ঞানের অতিরিক্ত ব্যবহার করতে শুরু করেছে। একটি ভাল ব্যাখ্যা যা আমি পেয়েছি তা হল দ্রুত স্ট্রিং শিক্স গাছের সাথে অনুসন্ধান করা , কিন্তু আলগোরিদিমের কিছু দিক এবং তিনি আলগোরিদিমের কিছু দিককে চকচকে দেখেন।

স্ট্যাক ওভারফ্লোে এই অ্যালগরিদমের ধাপে ধাপে ব্যাখ্যাটি আমার পাশাপাশি অনেকের জন্য অমূল্য হবে, আমি নিশ্চিত।

রেফারেন্সের জন্য এখানে অ্যালগরিদমের ইউকোনের কাগজ রয়েছে: http://www.cs.helsinki.fi/u/ukkonen/SuffixT1withFigs.pdf

আমার মৌলিক বোঝার, এতদূর:

  • আমি একটি প্রদত্ত স্ট্রিং টি প্রতিটি উপসর্গ পি মাধ্যমে পুনরাবৃত্তি করতে হবে
  • আমি prefix P প্রতিটি suffix S এর মাধ্যমে পুনরাবৃত্তি করতে হবে এবং গাছ যুক্ত করতে হবে
  • বৃক্ষের জন্য যথোপযুক্ত সৃষ্টিকর্তা যোগ করার জন্য, আমি S- তে প্রতিটি অক্ষরের মধ্য দিয়ে পুনরাবৃত্তি করতে হবে, এটি একটি বিদ্যমান শাখাটি হ্রাস করে যা বিদ্যমান শাখাটি ঘুরে বেড়ায় যা S- তে অক্ষর C এর একই সেট দিয়ে শুরু হয় এবং সম্ভবত যখন প্রান্তিক নোডগুলিতে একটি প্রান্ত বিভক্ত করে। প্রত্যয় একটি ভিন্ন চরিত্র পৌঁছানোর, অথবা নিচে পদব্রজে ভ্রমণ কোন মিলে প্রান্ত ছিল। সি এর জন্য হাঁটতে পাওয়া যায় না এমন কোন মিল পাওয়া গেলে, সিটির জন্য একটি নতুন পাতা প্রান্ত তৈরি করা হয়।

মৌলিক অ্যালগরিদমটি O (n 2 ) বলে মনে করা হয়, যা বেশিরভাগ ব্যাখ্যাগুলিতে উল্লেখ করা হয়েছে, যেমনটি আমাদের সমস্ত উপসর্গের মধ্য দিয়ে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে যেতে হবে। Ukkonen এর অ্যালগরিদম স্পষ্টতই অনন্য কারণ তিনি ব্যবহার করা প্রতিক্রিয়া পয়েন্টার কৌশল, যদিও আমি মনে করি যে আমি বুঝতে সমস্যা হচ্ছে কি।

আমি বুঝতে সমস্যা হচ্ছে:

  • ঠিক কখন এবং কিভাবে "সক্রিয় বিন্দু" নির্ধারিত, ব্যবহৃত এবং পরিবর্তিত হয়
  • অ্যালগরিদম ক্যানোনিআশন দৃষ্টিভঙ্গি সঙ্গে কি ঘটছে
  • আমি যে প্রয়োগগুলি দেখেছি তা ব্যাবহারকারী ব্যাবহারকারীগুলিকে "ঠিক করার" প্রয়োজন

এখানে সম্পূর্ণ সি # সোর্স কোড। এটি কেবল সঠিকভাবে কাজ করে না, তবে স্বয়ংক্রিয় ক্যানোনিয়েশনকে সমর্থন করে এবং আউটপুটের একটি নিখরচায় বর্ণন গ্রাফ রেন্ডার করে। উত্স কোড এবং নমুনা আউটপুট হয়:

https://gist.github.com/2373868

2017-11-04 আপডেট করুন

বহু বছর পরে আমি প্রত্যয় গাছের জন্য একটি নতুন ব্যবহার খুঁজে পেয়েছি, এবং জাভাস্ক্রিপ্টে আলগোরিদিম প্রয়োগ করেছি। নিচে দেওয়া হয়েছে। এটা বাগ মুক্ত হতে হবে। এটি একটি জেএস ফাইলের মধ্যে ডাম্প করুন, npm install chalk একই অবস্থান থেকে npm install chalk এবং তারপর কিছু রঙিন আউটপুট দেখতে node.js দিয়ে চালান। ডিবিগিং কোড ছাড়া, একই জিস্টে একটি ছিনতাই করা সংস্করণ রয়েছে।

https://gist.github.com/axefrog/c347bf0f5e0723cbd09b1aaed6ec6fc6


নিম্নরূপ আমার অন্তর্দৃষ্টি:

মূল লুপের k পুনরাবৃত্তি করার পরে আপনি একটি যথোপযুক্ত সৃষ্টিকর্তা তৈরি করেছেন যা সম্পূর্ণ স্ট্রিংগুলির সমস্ত প্রতিক্রিয়া ধারণ করে যা প্রথম অক্ষরগুলিতে শুরু হয়।

প্রারম্ভে, এর অর্থ হল প্রিক্স ট্রিটিতে একটি একক রুট নোড রয়েছে যা সমগ্র স্ট্রিংকে প্রতিনিধিত্ব করে (এটি শুধুমাত্র একমাত্র প্রতীক যা 0 থেকে শুরু হয়)।

লেন (স্ট্রিং) পুনরাবৃত্তি করার পরে আপনার একটি প্রতিক্রিয়া গাছ রয়েছে যা সমস্ত প্রতিক্রিয়া ধারণ করে।

লুপ সময় কী সক্রিয় পয়েন্ট। আমার অনুমান হল যে এটি যথোপযুক্ত সৃষ্টিকর্তাটির গভীরতম বিন্দুটিকে প্রতিনিধিত্ব করে যা স্ট্রিংয়ের প্রথম অক্ষরগুলির যথাযথ অনুভূতির সাথে সম্পর্কিত। (আমি মনে করি যথোপযুক্ত সৃষ্টিকর্তা সম্পূর্ণ স্ট্রিং হতে পারে না।)

উদাহরণস্বরূপ, ধরুন আপনি অক্ষর 'abcabc' দেখেছেন। সক্রিয় বিন্দুটি যথোপযুক্ত সৃষ্টিকর্তা 'abc' সংশ্লিষ্ট গাছের বিন্দুকে উপস্থাপন করবে।

সক্রিয় বিন্দু দ্বারা প্রতিনিধিত্ব করা হয় (মূল, প্রথম, শেষ)। এর মানে এই যে আপনি বর্তমানে সেই গাছের বিন্দুতে যা আপনি নোড উত্স থেকে শুরু করে এবং স্ট্রিংয়ের অক্ষরে খাওয়ানো শুরু করেছেন [প্রথম: শেষ]

যখন আপনি একটি নতুন চরিত্র যোগ করেন তখন আপনি দেখতে পাবেন যে সক্রিয় বিন্দু বিদ্যমান গাছটিতে এখনও রয়েছে কিনা। যদি হয় তাহলে আপনি সম্পন্ন করা হয়। অন্যথায় আপনাকে সক্রিয় বিন্দুতে প্রতিক্রিয়া গাছের জন্য একটি নতুন নোড যুক্ত করতে হবে, পরবর্তী ছোট্ট মিলের ফাঁদে ফিরতে হবে এবং আবার চেক করুন।

দ্রষ্টব্য 1: যথোপযুক্ত সৃষ্টিকর্তা প্রতিটি নোডের জন্য পরবর্তী ছোট্ট মিলের একটি লিঙ্ক দেয়।

দ্রষ্টব্য 2: যখন আপনি একটি নতুন নোড এবং ফোলব্যাক যোগ করেন তখন আপনি নতুন নোডের জন্য একটি নতুন প্রতিক্রিয়া পয়েন্টার যোগ করেন। এই প্রতিক্রিয়া পয়েন্টার জন্য গন্তব্য সংক্ষিপ্ত সক্রিয় পয়েন্টে নোড হবে। এই নোডটি ইতিমধ্যে বিদ্যমান থাকবে, অথবা এই fallback লুপ পরবর্তী পুনরাবৃত্তি উপর তৈরি করা হবে।

দ্রষ্টব্য 3: ক্যানোনিয়েশন অংশটি কেবল সক্রিয় বিন্দুটি পরীক্ষা করার সময় সংরক্ষণ করে। উদাহরণস্বরূপ, ধরুন আপনি সর্বদা উত্স = 0 ব্যবহার করেছেন, এবং শুধুমাত্র প্রথম এবং শেষ পরিবর্তন করেছেন। সক্রিয় বিন্দুটি পরীক্ষা করার জন্য আপনাকে প্রতিটি অন্তর্বর্তী নোড বরাবর প্রতিবার ট্রিপ অনুসরণ করতে হবে। শেষ নোড থেকে দূরত্বটি রেকর্ড করে এই পাথটি অনুসরণ করার ফলাফলটি ক্যাশে করার অর্থ ধার্য করে।

আপনি কি "ফিক্স" বাঁধার ভেরিয়েবলের দ্বারা বোঝাতে চান তার একটি কোড উদাহরণ দিতে পারেন?

স্বাস্থ্য সতর্কতা: আমি এই অ্যালগরিদমটিকে বিশেষভাবে বোঝার জন্য খুব কঠিন বলে অনুগ্রহ করে অনুগ্রহ করে বুঝতে পারেন যে এই অন্তর্দৃষ্টিটি সমস্ত গুরুত্বপূর্ণ বিশদগুলিতে ভুল হতে পারে ...


@ জোগোজাপান দ্বারা ভালভাবে ব্যাখ্যা করা টিউটোরিয়ালের জন্য ধন্যবাদ, আমি পাইথন এ অ্যালগরিদম বাস্তবায়ন করেছি।

@ জোগোজাপান দ্বারা উল্লেখ করা কয়েকটি ছোটখাট সমস্যাগুলি আমি প্রত্যাশিত চেয়ে আরো পরিশীলিত হতে দেখি, এবং খুব সতর্কতার সঙ্গে চিকিত্সা করা দরকার। আমার বাস্তবায়ন যথেষ্ট শক্তিশালী (বেশিরভাগ) আমি এটি বেশ কয়েক দিন ব্যয় করেছি। সমস্যা এবং সমাধান নীচে তালিকাভুক্ত করা হয়:

  1. Remainder > 0 সাথে শেষ Remainder > 0 এটি সম্পূর্ণরূপে সম্পূর্ণ অ্যালগরিদমের শেষ না হওয়া পর্যন্ত এই পরিস্থিতিটি উদ্ভূত ধাপেও ঘটতে পারে। যখন এটি ঘটবে, তখন আমরা অবশিষ্ট, অ্যাক্টনড, অ্যাক্টেজ এবং অ্যাকলাইট লাইট অপরিবর্তিত রাখতে পারব, বর্তমান প্রারম্ভিক ধাপটি শেষ করতে এবং মূল স্ট্রিংয়ের পরবর্তী গৃহস্থালির বর্তমান পাথের উপর নির্ভর করে যদি কোনও ফাঁক বা প্রকাশ করা হয় তবে অন্য ধাপটি শুরু করুন। না.

  2. নোডের উপর ল্যাপ করুন: যখন আমরা একটি প্রতিক্রিয়া লিঙ্ক অনুসরণ করি, সক্রিয় বিন্দুটি আপডেট করুন, এবং তারপরে এটি সক্রিয় করুন যে এটি সক্রিয়_লম্বন উপাদান নতুন সক্রিয়_ নোডের সাথে ভালভাবে কাজ করে না। আমরা বিভক্ত ডান জায়গা থেকে সরানো বা একটি পাতা সন্নিবেশ করা আছে। এই প্রক্রিয়াটি সহজবোধ্য নাও হতে পারে কারণ চলমান চলমান দৈর্ঘ্য এবং অ্যাক্টিগেজ চলাকালীন চলতে থাকে, যখন আপনাকে রুট নোডে ফিরে যেতে হয়, তখন সেই পদক্ষেপগুলির কারণে অ্যাক্টেজ এবং অ্যাকল্যাণ্ডলাইট ভুল হতে পারে। আমরা যে তথ্য রাখা অতিরিক্ত পরিবর্তনশীল (গুলি) প্রয়োজন।

অন্য দুটো সমস্যা @ মনগোনভ দ্বারা কিছুটা ইঙ্গিত করা হয়েছে

  1. স্প্লিট ডিজেনেট করতে পারে যখন একটি প্রান্ত বিভাজন করার চেষ্টা করা হয়, কিছু সময় আপনি স্প্লিট অপারেশনটি একটি নোডের উপর সঠিকভাবে পাবেন। সেই ক্ষেত্রে আমাদের কেবল সেই নোডের জন্য একটি নতুন পাতা যুক্ত করতে হবে, এটি একটি স্ট্যান্ডার্ড এজ স্প্লিট অপারেশন হিসাবে গ্রহণ করুন, যার মানে যদি কোনও প্রিক্স লিঙ্ক থাকে তবে এটি সামঞ্জস্যপূর্ণভাবে রক্ষণাবেক্ষণ করা উচিত।

  2. লুকানো Suffix লিংক আরেকটি বিশেষ কেস যা সমস্যা 1 এবং সমস্যা 2 দ্বারা ব্যয় করা হয়। কখনও কখনও আমরা বিভক্ত জন্য ডান বিন্দুতে বিভিন্ন নোডের উপর হপ করতে হবে, যদি আমরা অবশিষ্ট স্ট্রিং এবং পাথ লেবেলগুলির তুলনা করে সরানো সঠিক পয়েন্ট অতিক্রম করতে পারি। যে ক্ষেত্রে প্রতিক্রিয়া লিংক unintentionally অবহেলা করা হবে, যদি সেখানে থাকা উচিত। এগিয়ে যাওয়ার সময় সঠিক বিন্দুটি স্মরণ করে এড়ানো যেতে পারে। স্প্লিট নোড ইতিমধ্যে বিদ্যমান থাকলে প্রতিক্রিয়া লিঙ্কটি বজায় রাখা উচিত, অথবা এমনকি সমস্যাটি 1 প্রদর্শিত পদক্ষেপের সময় ঘটে।

অবশেষে, পাইথন আমার বাস্তবায়ন নিম্নরূপ:

টিপস: এটি উপরে কোডে একটি সাদাসিধা গাছ মুদ্রণ ফাংশন অন্তর্ভুক্ত, যা ডিবাগিং যখন খুব গুরুত্বপূর্ণ । এটি আমাকে অনেক সময় বাঁচায় এবং বিশেষ ক্ষেত্রে সনাক্ত করার জন্য সুবিধাজনক।


@ জোগোজাপান আপনি দুর্দান্ত ব্যাখ্যা এবং কল্পনা নিয়ে এসেছেন। কিন্তু @ মাকাগোভোভ উল্লেখ করেছে যে এটি সেটিক্স লিঙ্কে সেটিংস সম্পর্কিত কিছু নিয়ম অনুপস্থিত। http://brenden.github.io/ukkonen-animation/ শব্দ 'aabaaabb' মাধ্যমে ধাপে ধাপে যাওয়ার সময় এটি দুর্দান্ত ভাবে দৃশ্যমান । যখন আপনি ধাপ 10 থেকে ধাপ 11 এ যান, নোড 5 থেকে নোড 2 পর্যন্ত কোনও প্রতিক্রিয়া লিঙ্ক নেই তবে সক্রিয় বিন্দু হঠাৎ সেখানে চলে যায়।

@ মাকাগোভভ যেহেতু আমি জাভা জগতে বাস করছি আমিও এসটি বিল্ডিং ওয়ার্কফ্লো বুঝতে আপনার বাস্তবায়ন অনুসরণ করার চেষ্টা করেছি কিন্তু আমার পক্ষে এটি কঠিন ছিল:

  • নোড সঙ্গে প্রান্ত মিশ্রন
  • পরিবর্তে সূচক পয়েন্টার ব্যবহার করে
  • বিবৃতি বিরতি;
  • বিবৃতি অবিরত;

তাই আমি জাভাতে এই ধরনের বাস্তবায়নের সাথে শেষ হয়েছি যা আমি আশা করি পরিষ্কারভাবে সব পদক্ষেপ প্রতিফলিত করে এবং অন্যান্য জাভা মানুষের জন্য শেখার সময় হ্রাস করবে:

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class ST {

  public class Node {
    private final int id;
    private final Map<Character, Edge> edges;
    private Node slink;

    public Node(final int id) {
        this.id = id;
        this.edges = new HashMap<>();
    }

    public void setSlink(final Node slink) {
        this.slink = slink;
    }

    public Map<Character, Edge> getEdges() {
        return this.edges;
    }

    public Node getSlink() {
        return this.slink;
    }

    public String toString(final String word) {
        return new StringBuilder()
                .append("{")
                .append("\"id\"")
                .append(":")
                .append(this.id)
                .append(",")
                .append("\"slink\"")
                .append(":")
                .append(this.slink != null ? this.slink.id : null)
                .append(",")
                .append("\"edges\"")
                .append(":")
                .append(edgesToString(word))
                .append("}")
                .toString();
    }

    private StringBuilder edgesToString(final String word) {
        final StringBuilder edgesStringBuilder = new StringBuilder();
        edgesStringBuilder.append("{");
        for(final Map.Entry<Character, Edge> entry : this.edges.entrySet()) {
            edgesStringBuilder.append("\"")
                    .append(entry.getKey())
                    .append("\"")
                    .append(":")
                    .append(entry.getValue().toString(word))
                    .append(",");
        }
        if(!this.edges.isEmpty()) {
            edgesStringBuilder.deleteCharAt(edgesStringBuilder.length() - 1);
        }
        edgesStringBuilder.append("}");
        return edgesStringBuilder;
    }

    public boolean contains(final String word, final String suffix) {
        return !suffix.isEmpty()
                && this.edges.containsKey(suffix.charAt(0))
                && this.edges.get(suffix.charAt(0)).contains(word, suffix);
    }
  }

  public class Edge {
    private final int from;
    private final int to;
    private final Node next;

    public Edge(final int from, final int to, final Node next) {
        this.from = from;
        this.to = to;
        this.next = next;
    }

    public int getFrom() {
        return this.from;
    }

    public int getTo() {
        return this.to;
    }

    public Node getNext() {
        return this.next;
    }

    public int getLength() {
        return this.to - this.from;
    }

    public String toString(final String word) {
        return new StringBuilder()
                .append("{")
                .append("\"content\"")
                .append(":")
                .append("\"")
                .append(word.substring(this.from, this.to))
                .append("\"")
                .append(",")
                .append("\"next\"")
                .append(":")
                .append(this.next != null ? this.next.toString(word) : null)
                .append("}")
                .toString();
    }

    public boolean contains(final String word, final String suffix) {
        if(this.next == null) {
            return word.substring(this.from, this.to).equals(suffix);
        }
        return suffix.startsWith(word.substring(this.from,
                this.to)) && this.next.contains(word, suffix.substring(this.to - this.from));
    }
  }

  public class ActivePoint {
    private final Node activeNode;
    private final Character activeEdgeFirstCharacter;
    private final int activeLength;

    public ActivePoint(final Node activeNode,
                       final Character activeEdgeFirstCharacter,
                       final int activeLength) {
        this.activeNode = activeNode;
        this.activeEdgeFirstCharacter = activeEdgeFirstCharacter;
        this.activeLength = activeLength;
    }

    private Edge getActiveEdge() {
        return this.activeNode.getEdges().get(this.activeEdgeFirstCharacter);
    }

    public boolean pointsToActiveNode() {
        return this.activeLength == 0;
    }

    public boolean activeNodeIs(final Node node) {
        return this.activeNode == node;
    }

    public boolean activeNodeHasEdgeStartingWith(final char character) {
        return this.activeNode.getEdges().containsKey(character);
    }

    public boolean activeNodeHasSlink() {
        return this.activeNode.getSlink() != null;
    }

    public boolean pointsToOnActiveEdge(final String word, final char character) {
        return word.charAt(this.getActiveEdge().getFrom() + this.activeLength) == character;
    }

    public boolean pointsToTheEndOfActiveEdge() {
        return this.getActiveEdge().getLength() == this.activeLength;
    }

    public boolean pointsAfterTheEndOfActiveEdge() {
        return this.getActiveEdge().getLength() < this.activeLength;
    }

    public ActivePoint moveToEdgeStartingWithAndByOne(final char character) {
        return new ActivePoint(this.activeNode, character, 1);
    }

    public ActivePoint moveToNextNodeOfActiveEdge() {
        return new ActivePoint(this.getActiveEdge().getNext(), null, 0);
    }

    public ActivePoint moveToSlink() {
        return new ActivePoint(this.activeNode.getSlink(),
                this.activeEdgeFirstCharacter,
                this.activeLength);
    }

    public ActivePoint moveTo(final Node node) {
        return new ActivePoint(node, this.activeEdgeFirstCharacter, this.activeLength);
    }

    public ActivePoint moveByOneCharacter() {
        return new ActivePoint(this.activeNode,
                this.activeEdgeFirstCharacter,
                this.activeLength + 1);
    }

    public ActivePoint moveToEdgeStartingWithAndByActiveLengthMinusOne(final Node node,
                                                                       final char character) {
        return new ActivePoint(node, character, this.activeLength - 1);
    }

    public ActivePoint moveToNextNodeOfActiveEdge(final String word, final int index) {
        return new ActivePoint(this.getActiveEdge().getNext(),
                word.charAt(index - this.activeLength + this.getActiveEdge().getLength()),
                this.activeLength - this.getActiveEdge().getLength());
    }

    public void addEdgeToActiveNode(final char character, final Edge edge) {
        this.activeNode.getEdges().put(character, edge);
    }

    public void splitActiveEdge(final String word,
                                final Node nodeToAdd,
                                final int index,
                                final char character) {
        final Edge activeEdgeToSplit = this.getActiveEdge();
        final Edge splittedEdge = new Edge(activeEdgeToSplit.getFrom(),
                activeEdgeToSplit.getFrom() + this.activeLength,
                nodeToAdd);
        nodeToAdd.getEdges().put(word.charAt(activeEdgeToSplit.getFrom() + this.activeLength),
                new Edge(activeEdgeToSplit.getFrom() + this.activeLength,
                        activeEdgeToSplit.getTo(),
                        activeEdgeToSplit.getNext()));
        nodeToAdd.getEdges().put(character, new Edge(index, word.length(), null));
        this.activeNode.getEdges().put(this.activeEdgeFirstCharacter, splittedEdge);
    }

    public Node setSlinkTo(final Node previouslyAddedNodeOrAddedEdgeNode,
                           final Node node) {
        if(previouslyAddedNodeOrAddedEdgeNode != null) {
            previouslyAddedNodeOrAddedEdgeNode.setSlink(node);
        }
        return node;
    }

    public Node setSlinkToActiveNode(final Node previouslyAddedNodeOrAddedEdgeNode) {
        return setSlinkTo(previouslyAddedNodeOrAddedEdgeNode, this.activeNode);
    }
  }

  private static int idGenerator;

  private final String word;
  private final Node root;
  private ActivePoint activePoint;
  private int remainder;

  public ST(final String word) {
    this.word = word;
    this.root = new Node(idGenerator++);
    this.activePoint = new ActivePoint(this.root, null, 0);
    this.remainder = 0;
    build();
  }

  private void build() {
    for(int i = 0; i < this.word.length(); i++) {
        add(i, this.word.charAt(i));
    }
  }

  private void add(final int index, final char character) {
    this.remainder++;
    boolean characterFoundInTheTree = false;
    Node previouslyAddedNodeOrAddedEdgeNode = null;
    while(!characterFoundInTheTree && this.remainder > 0) {
        if(this.activePoint.pointsToActiveNode()) {
            if(this.activePoint.activeNodeHasEdgeStartingWith(character)) {
                activeNodeHasEdgeStartingWithCharacter(character, previouslyAddedNodeOrAddedEdgeNode);
                characterFoundInTheTree = true;
            }
            else {
                if(this.activePoint.activeNodeIs(this.root)) {
                    rootNodeHasNotEdgeStartingWithCharacter(index, character);
                }
                else {
                    previouslyAddedNodeOrAddedEdgeNode = internalNodeHasNotEdgeStartingWithCharacter(index,
                            character, previouslyAddedNodeOrAddedEdgeNode);
                }
            }
        }
        else {
            if(this.activePoint.pointsToOnActiveEdge(this.word, character)) {
                activeEdgeHasCharacter();
                characterFoundInTheTree = true;
            }
            else {
                if(this.activePoint.activeNodeIs(this.root)) {
                    previouslyAddedNodeOrAddedEdgeNode = edgeFromRootNodeHasNotCharacter(index,
                            character,
                            previouslyAddedNodeOrAddedEdgeNode);
                }
                else {
                    previouslyAddedNodeOrAddedEdgeNode = edgeFromInternalNodeHasNotCharacter(index,
                            character,
                            previouslyAddedNodeOrAddedEdgeNode);
                }
            }
        }
    }
  }

  private void activeNodeHasEdgeStartingWithCharacter(final char character,
                                                    final Node previouslyAddedNodeOrAddedEdgeNode) {
    this.activePoint.setSlinkToActiveNode(previouslyAddedNodeOrAddedEdgeNode);
    this.activePoint = this.activePoint.moveToEdgeStartingWithAndByOne(character);
    if(this.activePoint.pointsToTheEndOfActiveEdge()) {
        this.activePoint = this.activePoint.moveToNextNodeOfActiveEdge();
    }
  }

  private void rootNodeHasNotEdgeStartingWithCharacter(final int index, final char character) {
    this.activePoint.addEdgeToActiveNode(character, new Edge(index, this.word.length(), null));
    this.activePoint = this.activePoint.moveTo(this.root);
    this.remainder--;
    assert this.remainder == 0;
  }

  private Node internalNodeHasNotEdgeStartingWithCharacter(final int index,
                                                         final char character,
                                                         Node previouslyAddedNodeOrAddedEdgeNode) {
    this.activePoint.addEdgeToActiveNode(character, new Edge(index, this.word.length(), null));
    previouslyAddedNodeOrAddedEdgeNode = this.activePoint.setSlinkToActiveNode(previouslyAddedNodeOrAddedEdgeNode);
    if(this.activePoint.activeNodeHasSlink()) {
        this.activePoint = this.activePoint.moveToSlink();
    }
    else {
        this.activePoint = this.activePoint.moveTo(this.root);
    }
    this.remainder--;
    return previouslyAddedNodeOrAddedEdgeNode;
  }

  private void activeEdgeHasCharacter() {
    this.activePoint = this.activePoint.moveByOneCharacter();
    if(this.activePoint.pointsToTheEndOfActiveEdge()) {
        this.activePoint = this.activePoint.moveToNextNodeOfActiveEdge();
    }
  }

  private Node edgeFromRootNodeHasNotCharacter(final int index,
                                             final char character,
                                             Node previouslyAddedNodeOrAddedEdgeNode) {
    final Node newNode = new Node(idGenerator++);
    this.activePoint.splitActiveEdge(this.word, newNode, index, character);
    previouslyAddedNodeOrAddedEdgeNode = this.activePoint.setSlinkTo(previouslyAddedNodeOrAddedEdgeNode, newNode);
    this.activePoint = this.activePoint.moveToEdgeStartingWithAndByActiveLengthMinusOne(this.root,
            this.word.charAt(index - this.remainder + 2));
    this.activePoint = walkDown(index);
    this.remainder--;
    return previouslyAddedNodeOrAddedEdgeNode;
  }

  private Node edgeFromInternalNodeHasNotCharacter(final int index,
                                                 final char character,
                                                 Node previouslyAddedNodeOrAddedEdgeNode) {
    final Node newNode = new Node(idGenerator++);
    this.activePoint.splitActiveEdge(this.word, newNode, index, character);
    previouslyAddedNodeOrAddedEdgeNode = this.activePoint.setSlinkTo(previouslyAddedNodeOrAddedEdgeNode, newNode);
    if(this.activePoint.activeNodeHasSlink()) {
        this.activePoint = this.activePoint.moveToSlink();
    }
    else {
        this.activePoint = this.activePoint.moveTo(this.root);
    }
    this.activePoint = walkDown(index);
    this.remainder--;
    return previouslyAddedNodeOrAddedEdgeNode;
  }

  private ActivePoint walkDown(final int index) {
    while(!this.activePoint.pointsToActiveNode()
            && (this.activePoint.pointsToTheEndOfActiveEdge() || this.activePoint.pointsAfterTheEndOfActiveEdge())) {
        if(this.activePoint.pointsAfterTheEndOfActiveEdge()) {
            this.activePoint = this.activePoint.moveToNextNodeOfActiveEdge(this.word, index);
        }
        else {
            this.activePoint = this.activePoint.moveToNextNodeOfActiveEdge();
        }
    }
    return this.activePoint;
  }

  public String toString(final String word) {
    return this.root.toString(word);
  }

  public boolean contains(final String suffix) {
    return this.root.contains(this.word, suffix);
  }

  public static void main(final String[] args) {
    final String[] words = {
            "abcabcabc$",
            "abc$",
            "abcabxabcd$",
            "abcabxabda$",
            "abcabxad$",
            "aabaaabb$",
            "aababcabcd$",
            "ababcabcd$",
            "abccba$",
            "mississipi$",
            "abacabadabacabae$",
            "abcabcd$",
            "00132220$"
    };
    Arrays.stream(words).forEach(word -> {
        System.out.println("Building suffix tree for word: " + word);
        final ST suffixTree = new ST(word);
        System.out.println("Suffix tree: " + suffixTree.toString(word));
        for(int i = 0; i < word.length() - 1; i++) {
            assert suffixTree.contains(word.substring(i)) : word.substring(i);
        }
    });
  }
}




suffix-tree