Decode String
تكتب السلسلة المُرمَّزة النص المتكرر بالشكل k[text]: يُكتب text عدد k من المرات متتالية. يمكن أن تتداخل المجموعات، لذا فإن 2[a3[b]] تمثل abbbabbb. بالنظر إلى سلسلة مُرمَّزة s، أعد السلسلة المفكوكة الترميز.
تبقى الأحرف الواقعة خارج جميع الأقواس كما هي. كل عدد صحيح موجب، ويُكتب مباشرةً قبل [، ولا تظهر الأرقام في أي موضع آخر.
الدالة
- sstring
- السلسلة المُرمَّزة
- تُرجعstring
- السلسلة النصية المفكوكة الترميز
القيود
1 ≤ s.length ≤ 104sيحتوي فقط على أحرف إنجليزية صغيرة وأرقام و[و].sترميز صالح: يتبع كل[عددٌ، وله]مطابق، ولا توجد أقواس فارغة.- كل عدد
kيحقق1 ≤ k ≤ 300ولا يحتوي على صفر في بدايته. - يمكن أن تتداخل الأقواس بعمق يصل إلى 100 مستوى كحد أقصى.
- يحتوي النص بعد فك ترميزه على
5 × 104أحرف كحد أقصى.
أمثلة
- المدخلات
- s = "2[ab]3[c]x"
- المخرجات
- "ababcccx"
- الشرح
- يعطي
2[ab]الناتجabab، ويعطي3[c]الناتجccc. يقعxخارج جميع الأقواس ويُنقل كما هو:ababcccx.
- المدخلات
- s = "2[x3[yz]]"
- المخرجات
- "xyzyzyzxyzyzyz"
- الشرح
- فكّ ترميز الجزء الداخلي أولًا:
3[yz]هوyzyzyz، لذا يصبح الجزء الخارجيxyzyzyz. وعند كتابته مرتين، يكونxyzyzyzxyzyzyz.
- المدخلات
- s = "q10[w]e"
- المخرجات
- "qwwwwwwwwwwe"
- الشرح
- العدد هو
10، ويُقرأ من رقمين، لذا يظهرwعشر مرات بينqوe.
سؤال إضافي
قد تكون السلسلة المفككة أطول بكثير من المُدخل. كيف ستُعيد الحرف عند الموضع i دون إنشاء السلسلة، عندما قد يصل طولها إلى 10^18؟
تلميحات
افتحها واحدًا تلو الآخر. كل تلميح يكشف أكثر قليلًا.
لا يمكنك كتابة
3[...]قبل أن تعرف ما بداخل الأقواس، وقد يحتوي ما بداخلها على مجموعات أخرى. ما نوع المجموعة التي يمكنك فك ترميزها دائمًا على الفور؟يمكن توسيع المجموعة التي لا تحتوي بداخلها على مجموعة أخرى دفعةً واحدة، لذا اعمل من الداخل إلى الخارج. عندما يصل
]، تكتمل المجموعة التي يغلقها، وتحتاج إلى النص والعدد اللذين كانا بانتظارك قبل[الخاص بها.امسح النص مرة واحدة، مع الاحتفاظ بالنص الذي بُني حتى الآن والعدد الذي تتم قراءته. عند
[، ادفع كليهما إلى المكدس وابدأ من جديد. عند]، أخرج القيمتين منه وألحق النص الحالي، مكررًا، بالنص المُخرج. ابنِ كل عدد رقمًا رقمًا كي يعمل كل من10و300.
الحل
لا يمكنك كتابة نسخ المجموعة قبل أن تعرف محتواها، وقد يحتوي هذا المحتوى على مجموعات أخرى. لذا تُنهي كل طريقة المجموعات الأعمق أولًا: أعد كتابة السلسلة من الداخل إلى الخارج، أو استدعِ الدالة递归يًا على كل محتوى، أو احتفظ بالمجموعات الخارجية غير المكتملة في مكدس. هنا n هو طول الإدخال، وm هو الطول بعد فك الترميز، وd هو عمق التداخل.
وسّع المجموعة الأعمق، ثم كرّر
الفكرة
ابحث عن مجموعة لا توجد بداخلها مجموعة أخرى، واكتب نسخها في موضعها، ثم أعد البحث. أول ] يغلق دائمًا مجموعة كهذه: لم تُغلق أي مجموعة قبله، لذا لا يحتوي جسمها على أي قوس. أما [ الخاص بها فهو الأقرب إلى يساره، والعدد هو سلسلة الأرقام التي تسبقه. في 2[x3[yz]]، يتم توسيع 3[yz] أولًا، لينتج 2[xyzyzyz]، ثم يؤدي توسيع آخر إلى الإجابة.
تعيد كل عملية توسيع بناء السلسلة بأكملها. مع وجود b مجموعات وناتج مكوّن من m حرفًا، قد يصل عدد النسخ إلى b × m: فوجود 1,000 مجموعة تُفك إلى 50,000 حرف يعني ما يصل إلى 5 × 10^7.
الخوارزمية
- اعثر على أول
]. إذا لم يوجد، فأعِد السلسلة. - تحرّك إلى اليسار حتى أقرب
[. النص بينهما هو المحتوى. - اقرأ الأرقام التي تسبق
[على أنها العددk. - استبدل كل شيء من أول رقم إلى
]بالمحتوى مكرّرًاkمرة، وكرّر ذلك.
def decodeString(s):
# Expand one innermost group at a time until no bracket is left.
while True:
close = s.find("]")
if close == -1:
return s
# The first ']' closes a group with no group inside it,
# and the nearest '[' to its left opens that group.
open_ = s.rfind("[", 0, close)
start = open_
while start > 0 and s[start - 1].isdigit():
start -= 1
times = int(s[start:open_])
s = s[:start] + s[open_ + 1:close] * times + s[close + 1:]التحليل التنازلي العودي
الفكرة
السلسلة المُرمَّزة هي تسلسل من الأحرف والمجموعات، ومحتوى المجموعة هو بدوره سلسلة مُرمَّزة. لذا، تقرأ دالة واحدة، decode، من موضع مشترك حتى تصل إلى ] أو نهاية الإدخال، وتستدعي نفسها لكل محتوى مجموعة. يتوقف هذا الاستدعاء عند ] المطابق، لأن كل ] أعمق استهلكه استدعاء أعمق. بالنسبة إلى 2[x3[yz]]، يُعيد الاستدعاء الأعمق yz، ويُعيد الاستدعاء الأوسط xyzyzyz، ثم تكتب الدالة الخارجية ذلك مرتين.
تُقرأ كل محرف من الإدخال مرة واحدة، لكن يُنسخ محرف الإخراج مرة لكل مجموعة تحيط به: زمن O(n + m·d). يصل عمق الاستدعاء التكراري إلى d استدعاءات.
الخوارزمية
- احتفظ بموضع واحد
posتشترك فيه جميع الاستدعاءات، وابدأ من 0. - تكرّر
decode()ما دامposداخل السلسلة وليس عند]. - عند مصادفة حرف، أضِفه وانتقل إلى التالي.
- عند مصادفة رقم، اقرأ العدد كاملًا
k، وتجاوز[، واستدعِdecode()لمعالجة المحتوى، وتجاوز]، ثم أضِف المحتوىkمرات. - أعِد ما تم بناؤه. يعيد الاستدعاء الأول السلسلة المفككة.
def decodeString(s):
pos = 0
def decode():
# Read from pos up to the ']' that closes this level, or the end.
nonlocal pos
parts = []
while pos < len(s) and s[pos] != "]":
if s[pos].isdigit():
times = 0
while s[pos].isdigit():
times = times * 10 + int(s[pos])
pos += 1
pos += 1 # skip '['
inner = decode() # the group's body, fully decoded
pos += 1 # skip ']'
parts.append(inner * times)
else:
parts.append(s[pos])
pos += 1
return "".join(parts)
return decode()مرور واحد باستخدام مكدس
الفكرة
تحتفظ العودية بقطعة نصية واحدة غير مكتملة لكل مجموعة مفتوحة ضمن إطارات الاستدعاء الخاصة بها. احتفظ بهذه القطع في مكدس خاص بك، وستصبح عملية فك الترميز حلقة واحدة. تحفظ [ النص الذي فُك ترميزه حتى الآن في هذا المستوى، مع عدد مرات التكرار، وتبدأ جسم المجموعة من نص فارغ. أما ] فتزيلهما من المكدس وتُلحق النسخ بالنص المحفوظ. تُغلق المجموعات بترتيب عكسي لترتيب فتحها، لذا يكون المستوى الذي تعود إليه ] دائمًا في أعلى المكدس.
في 2[x3[yz]]، تضع في المكدس (فارغ، 2)، ثم (x، 3). تزيل ] الأولى (x، 3)، ويصبح current هو xyzyzyz؛ ثم تزيل الأخيرة (فارغ، 2) وتضاعف النص. يتطابق العمل مع العودية، O(n + m·d)، لكن التداخل العميق ينمّي قائمة، لا مكدس الاستدعاءات.
الخوارزمية
- ابدأ بمكدس فارغ، و
currentفارغ، وcount = 0. - عند وجود رقم، عيّن
count = count × 10 + digit. - عند
[، ادفع (current،count) إلى المكدس، ثم أعد تعيين كليهما. - عند وجود حرف، أضِفه إلى
current. - عند
]، أزل (before،k) من المكدس، وعيّنcurrentإلىbeforeمتبوعًا بـkنسخ منcurrent. - أعِد
current.
def decodeString(s):
stack = [] # one entry per open '[': (the text before it, its count)
current = [] # pieces of the text at the current level
count = 0
for ch in s:
if ch.isdigit():
count = count * 10 + int(ch) # counts can have several digits
elif ch == "[":
stack.append((current, count))
current, count = [], 0
elif ch == "]":
before, times = stack.pop()
before.append("".join(current) * times)
current = before
else:
current.append(ch)
return "".join(current)
أخطاء شائعة وحالات حدّية
- اعتبار رقم واحد هو العدد كاملًا. في
q10[w]e، العدد هو 10؛ وأخذ الرقم الذي يسبق[فقط يكررwعدد 0 مرات. - عدم إعادة تعيين
countإلى 0 بعد دفعه. عندئذٍ تمتد أرقام المجموعة التالية إلى العدد القديم، لذا تُقرأ قيمة العدد الداخلية في2[a3[b]]على أنها 23. - وضع النسخ قبل النص المحفوظ. ناتج
ab2[c]هوabcc، وليسccab. - إسقاط الأحرف الموجودة في المستوى الأعلى. يجب أن يكون الحرف
xفي2[ab]3[c]xجزءًا من الإجابة. - إلحاق حرف واحد في كل مرة بسلسلة غير قابلة للتغيير. قد تؤدي كل عملية إلحاق إلى نسخ السلسلة بأكملها، لذا فإن إجابة من 50,000 حرف تتطلب أكثر من مليار عملية نسخ للأحرف. اجمع الأجزاء في قائمة أو في منشئ سلاسل.
أسئلة شائعة3
ما هو التعقيد الزمني لفك ترميز سلسلة نصية؟
تستغرق قراءة الإدخال O(n). يُنسخ كل حرف في الناتج مرة واحدة لكل مجموعة تحيط به، لذا يكون الإجمالي O(n + m·d)، حيث m هو الطول بعد فك الترميز وd هو عمق التداخل. عندما يكون كل عدد 2 على الأقل، يظل عدد عمليات النسخ أقل من 2m. لا توجد طريقة تتجاوز O(m)، لأن الإجابة تتكون من m حرفًا.
هل ينبغي أن تحلّ مسألة Decode String باستخدام الاستدعاء الذاتي أم باستخدام مكدّس؟
كلاهما يؤدي العمل نفسه. يتبع الاستدعاء الذاتي الصيغة مباشرةً، لأن جسم المجموعة عبارة عن سلسلة مُرمّزة بحد ذاته، وغالبًا ما يكون الأسرع في الكتابة. يعمل إصدار المكدس ضمن حلقة واحدة ويحتفظ بالمستويات غير المكتملة في قائمة، لذا لا يمكن لإدخال متداخل بعمق آلاف المستويات أن يتسبب في تجاوز مكدس الاستدعاءات.
كيف تتعامل مع الأعداد التي تتكوّن من أكثر من رقم واحد؟
كوِّن العدد أثناء قراءته: ابدأ من 0، ولكل رقم عيّن count = count × 10 + digit. عندما يصل [ يكون العدد قد اكتمل، لذا تعطي 300[a] العدد 300.
مسائل مشابهة
مسائل تعتمد على الأفكار نفسها. حلّ اثنتين أو ثلاث منها يثبّت النمط.
def decodeString(s):
# اكتب الشيفرة هنااختبار #1
اختبار #2
اختبار #3
وسيط
s = "2[ab]3[c]x"
الإخراج المتوقع
"ababcccx"