فهرست مقاله
وقتی یک رشتهٔ چسبیده را با واژهنامه میشکنیم، پیدا شدنِ یک مدخل در آغازِ متن پایانِ کار نیست. انتخابِ همان مدخل ممکن است ادامه را پرهزینه یا بیپوشش کند. این درس یک مدلِ کوچک برای جداسازی میسازد: تمامِ متن باید از مدخلهای موجود تشکیل شود و جمعِ هزینهٔ دادهشده کمینه باشد. هزینهها ساختگیاند؛ هیچ پیکره، مدلِ زبانی یا کیفیتِ جستوجوی واقعی در این آزمایش سنجیده نمیشود.
۱. واژهنامه، هزینه و مرزِ یک برشِ کامل

ورودیِ متن، str دقیق با صفر تا هشت نقطهٔ کد است. واژهنامه dict دقیق با حداکثر هشت مدخل دارد؛ هر کلید، str دقیقِ ناتهی با طولِ یک تا چهار و هر هزینه، int دقیقِ صفر تا نه است. bool و زیرکلاسها پذیرفته نمیشوند. واژهنامه همین ورودیِ آماده است؛ دریافتِ زبان، یادگیریِ هزینه و استخراجِ مدخل از سند در برنامه وجود ندارد.
یک پاسخِ کامل باید با چسباندنِ قطعهها دقیقاً رشتهٔ اولیه را بسازد. میان قطعهها شکاف یا همپوشانی نداریم؛ قطعهٔ تهی نیز مجاز نیست. برخلافِ انتخابِ چند بازهٔ دلخواهِ متن، نمیتوان قسمتِ ناشناخته را کنار گذاشت و بقیه را موفق نامید. هزینه بیواحد است و از جمعِ هزینهٔ مدخلهای انتخابشده میآید، حتی اگر یک مدخل چند بار تکرار شود.
صورتِ مسئلهٔ بازسازیِ متن در Stanford CS221 جداسازی را با جمعِ هزینهٔ واژهها و شکستِ تصمیمِ حریصانه مطرح میکند. آن تکلیف از هزینهٔ مثبتِ مدلِ آماری و جریمهٔ واژهٔ ناشناخته استفاده میکند. مدلِ تألیفیِ ما هزینهٔ صحیحِ نامنفی و واژهنامهٔ بسته دارد؛ از کدِ آماده، جستوجوی UCS یا مدلِ آن درس استفاده نمیکنیم.
طبق قراردادِ رشته و عملیاتِ دنباله در پایتون، طول و برشِ str بر نقاطِ کدِ Unicode عمل میکنند. پایانِ هر قطعه، اندیسِ بیرون از برش است. این واحد، بایتِ UTF-8 یا خوشهٔ نویسهای نیست؛ مثلاً حرف و علامتِ ترکیبی میتوانند دو نقطهٔ کد باشند و مدل اجازهٔ برش میان آنها را دارد.
۲. انتخابِ محلی را با کمترین هزینهٔ کل اشتباه نگیریم

برای متنِ abc، هزینههای a، bc، ab و c بهترتیب صفر، نه، دو و صفرند. گرفتنِ ارزانترین مدخلِ بعدی، a را انتخاب میکند و ادامهٔ bc هزینهٔ نه میدهد. اما ab و سپس c جمعِ دو دارند. هر دو پاسخ کاملاند؛ اختلاف از نگاهِ محلی به هزینه است، نه از حذفِ مخفیانهٔ بخشی از متن.
بلندترین تطبیق هم هدفِ کمترین هزینه را تضمین نمیکند. در متنِ abcd، مدخلهای abc و d هزینهٔ یک و نه دارند، اما ab و cd هرکدام دو هستند. شروع با قطعهٔ بلندتر به ده میرسد؛ دو قطعهٔ کوتاهتر جمعِ چهار میدهند. این اعداد فقط شاهدِ محاسباتیاند؛ آنها را احتمالِ یک عبارت یا نشانهٔ طبیعیتر بودنِ جمله نمینامیم.
تابع برای هر پایانِ ممکن، بهترین پاسخِ پیشوندِ متن را نگه میدارد. برای رسیدن به پایانِ تازه، همهٔ آغازهای ممکنِ قطعهٔ آخر با طولِ حداکثر چهار بررسی میشوند. اگر پیشوندِ قبلی پوششِ کامل داشته باشد و قطعهٔ آخر در واژهنامه باشد، هزینه و یک قطعه به پاسخِ قبلی اضافه میشود. خانهٔ صفر، پیشوندِ تهی با هزینه و تعدادِ صفر است.
کافی بودنِ این حالت به جمعپذیری و مستقل بودنِ هزینهٔ هر مدخل وابسته است. هر دو پاسخِ رسیده به یک موقعیت، ادامههای یکسانی دارند؛ پس نگه داشتنِ پاسخِ بهترِ همان موقعیت، ادامهٔ بهتری را حذف نمیکند. هزینهٔ وابسته به واژهٔ قبلی، محدودیتِ دستوری یا مدلِ زمینهدار در این استدلال وجود ندارد؛ افزودنِ آنها ممکن است حالتِ دیگری بخواهد.
۳. تساوی، متنِ پوششنیافته و نیمفاصله را صریح کنیم

هدف را یک سهتایی تعریف میکنیم: نخست هزینهٔ کل، سپس تعدادِ قطعه و سرانجام تاپلِ مرتبِ پایانِ قطعهها. مقایسهٔ تاپلها واژهنامهای است. در ab با هزینههای a برابرِ صفر، b برابرِ یک و ab برابرِ یک، تکقطعهٔ ab برنده است. اگر هزینه و تعداد هر دو برابر باشند، نخستین پایانِ متفاوتِ کوچکتر برنده میشود.
در abc، اگر a و bc و ab و c همگی هزینهٔ یک داشته باشند، دو پاسخِ دوقطعهای هزینهٔ دو دارند. پایانهای a و bc، تاپلِ یک و سه است؛ پایانهای ab و c، دو و سه. پاسخِ اول برنده میشود. این قاعده ترجیحِ زبانی یا ترتیبِ درجِ dict نیست؛ بیستوچهار ترتیبِ درجِ همین چهار مدخل باید همان نتیجه را بدهند.
متنِ تهی پاسخِ کاملِ تهی با هزینهٔ صفر دارد، اما متنِ غیرتهیِ بیپوشش وضعیتِ unsegmentable و هزینه و قطعههای null میدهد. صفرِ واقعی را با نبودِ پاسخ یکی نمیکنیم. تمامِ مدخلها پیش از این تصمیمها بررسی میشوند؛ حتی برای متنِ تهی، کلیدِ خالی یا هزینهٔ bool خطاست. کلیدِ خالی حذف نمیشود، زیرا پیشرویِ موقعیت را از بین میبرد.
نیمفاصله و فاصله عیناً در رشته باقی میمانند. برای a، نیمفاصله، b، وجودِ مدخلهای a و b کافی نیست؛ مدخلی باید خودِ نیمفاصله را هم بپوشاند. درسِ کلیدهای نرمالشدهٔ فارسی سیاستِ همسانسازی را جدا بررسی میکند. این برنامه نه آن سیاست را اجرا میکند و نه از نیمفاصله، مرزِ واژهٔ زبانی نتیجه میگیرد.
۴. کدِ کامل و مرجعِ مستقلِ تمامِ برشها

کدِ کامل را در dictionary_segmentation_cost.py ذخیره کنید. فقط کتابخانهٔ استاندارد لازم است؛ نسخهٔ واقعیِ مفسر در گزارش ثبت میشود و از نسخهٔ مستندات جداست. تابع، متن و dict را بازنویسی نمیکند و قطعهها را از رشتهٔ اصلی میبُرد. هیچ نرمالسازی، تبدیلِ نوع یا حدسِ واژهٔ ناشناخته در مسیرِ محاسبه پنهان نیست.
"""Own finite dictionary tiling; supplied costs are not language probabilities."""
from itertools import permutations, product
import json
import platform
import sys
MAX_TEXT_POINTS = 8
MAX_ENTRIES = 8
MAX_WORD_POINTS = 4
MAX_COST = 9
def _validate(text, lexicon):
if type(text) is not str:
raise TypeError("exact str text required")
if len(text) > MAX_TEXT_POINTS:
raise ValueError("text too long")
if type(lexicon) is not dict:
raise TypeError("exact dict lexicon required")
if len(lexicon) > MAX_ENTRIES:
raise ValueError("too many entries")
# Validate every entry, even if text is empty or the entry is never used.
for word, cost in lexicon.items():
if type(word) is not str:
raise TypeError("exact str entry required")
if not 1 <= len(word) <= MAX_WORD_POINTS:
raise ValueError("empty or long entry")
if type(cost) is not int:
raise TypeError("exact int cost required")
if not 0 <= cost <= MAX_COST:
raise ValueError("cost outside contract")
def segment_min_cost(text, lexicon):
"""Complete tiling, ordered by (cost, segment count, end-cut tuple).
Exact text0..8 codepoints; exact dict0..8 with exact str keys1..4 and
exact int costs0..9. No coercion, normalization or unknown-word fallback.
"""
_validate(text, lexicon)
best = [None] * (len(text) + 1)
best[0] = (0, 0, ())
for end in range(1, len(text) + 1):
for start in range(max(0, end - MAX_WORD_POINTS), end):
prefix = best[start]
word = text[start:end]
if prefix is None or word not in lexicon:
continue
candidate = (prefix[0] + lexicon[word], prefix[1] + 1,
prefix[2] + (end,))
if best[end] is None or candidate < best[end]:
best[end] = candidate
winner = best[-1]
if winner is None:
return {"status": "unsegmentable", "cost": None,
"tokens": None, "end_cuts": None}
cuts = winner[2]
starts = (0,) + cuts[:-1]
tokens = tuple(text[start:end] for start, end in zip(starts, cuts))
return {"status": "complete", "cost": winner[0],
"tokens": tokens, "end_cuts": cuts}
def all_cuts_reference(text, lexicon):
"""Valid-input oracle: partition the whole text by every internal-cut mask.
No prefix table, recurrence, main call or partial-result reuse. Every
partition independently forms literal pieces, validates all, then scores.
"""
candidates = []
if text:
for mask in range(1 << (len(text) - 1)):
cuts = tuple(i for i in range(1, len(text))
if mask & (1 << (i - 1))) + (len(text),)
bounds = (0,) + cuts
pieces = tuple(text[bounds[i]:bounds[i + 1]]
for i in range(len(cuts)))
if all(piece in lexicon for piece in pieces):
cost = sum(lexicon[piece] for piece in pieces)
candidates.append(((cost, len(pieces), cuts), pieces))
else:
candidates.append(((0, 0, ()), ()))
if not candidates:
return {"status": "unsegmentable", "cost": None,
"tokens": None, "end_cuts": None}
key, pieces = min(candidates)
return {"status": "complete", "cost": key[0],
"tokens": pieces, "end_cuts": key[2]}
def rejected(text, lexicon):
try:
segment_min_cost(text, lexicon)
except (TypeError, ValueError):
return True
return False
def run_example():
checks = {}
local_lexicon = {"a": 0, "bc": 9, "ab": 2, "c": 0}
local = segment_min_cost("abc", local_lexicon)
checks["global_cost_beats_locally_cheapest_next_word"] = (
local == {"status": "complete", "cost": 2,
"tokens": ("ab", "c"), "end_cuts": (2, 3)}
and local_lexicon["a"] + local_lexicon["bc"] == 9 > local["cost"])
longest_lexicon = {"abc": 1, "d": 9, "ab": 2, "cd": 2}
longest = segment_min_cost("abcd", longest_lexicon)
checks["global_cost_beats_longest_next_word"] = (
longest["tokens"] == ("ab", "cd") and longest["cost"] == 4
and longest_lexicon["abc"] + longest_lexicon["d"] == 10)
count_tie = segment_min_cost("ab", {"a": 0, "b": 1, "ab": 1})
checks["equal_cost_prefers_fewer_segments"] = (
count_tie["cost"] == 1 and count_tie["tokens"] == ("ab",)
and count_tie["end_cuts"] == (2,))
cut_lexicon = {"a": 1, "bc": 1, "ab": 1, "c": 1}
cut_tie = segment_min_cost("abc", cut_lexicon)
checks["equal_cost_count_prefers_lexicographic_complete_cut_tuple"] = (
cut_tie["cost"] == 2 and cut_tie["tokens"] == ("a", "bc")
and cut_tie["end_cuts"] == (1, 3) < (2, 3))
unknown = segment_min_cost("abx", {"a": 1, "b": 1})
empty = segment_min_cost("", {})
checks["complete_coverage_required_and_empty_success_is_not_failure"] = (
unknown == {"status": "unsegmentable", "cost": None,
"tokens": None, "end_cuts": None}
and segment_min_cost("a", {}) == unknown
and empty == {"status": "complete", "cost": 0,
"tokens": (), "end_cuts": ()})
zero = segment_min_cost("aaaa", {"a": 0, "aa": 0})
checks["zero_cost_nonempty_segments_advance_and_respect_count_tie"] = (
zero["cost"] == 0 and zero["tokens"] == ("aa", "aa")
and zero["end_cuts"] == (2, 4)
and segment_min_cost("a", {"a": 0})["status"] == "complete")
zwnj = segment_min_cost("a\u200cb", {"a\u200c": 1, "b": 1})
checks["zwnj_and_spaces_are_literal_not_automatic_boundaries_or_deleted"] = (
segment_min_cost("a\u200cb", {"a": 1, "b": 1}) == unknown
and zwnj["tokens"] == ("a\u200c", "b") and zwnj["end_cuts"] == (2, 3)
and segment_min_cost("a b", {"a": 1, "b": 1}) == unknown
and segment_min_cost("a b", {"a": 1, " ": 0, "b": 1})["end_cuts"] == (1, 2, 3))
emoji = "\U0001f642a"
unicode_result = segment_min_cost(emoji, {"\U0001f642": 1, "a": 0})
checks["codepoint_cuts_are_not_bytes_graphemes_or_canonical_normalization"] = (
len(emoji) == 2 and len(emoji.encode("utf-8")) == 5
and unicode_result["end_cuts"] == (1, 2)
and segment_min_cost("e\u0301", {"\u00e9": 0}) == unknown
and segment_min_cost("\u00e9", {"\u00e9": 0})["tokens"] == ("\u00e9",)
and segment_min_cost("e\u0301", {"e": 0, "\u0301": 0})["tokens"] == ("e", "\u0301"))
universe = ("a", "b", "ab", "ba")
family_ok, family_cases, dictionaries = True, 0, 0
for states in product((-1, 0, 2), repeat=4):
lexicon = {word: cost for word, cost in zip(universe, states) if cost >= 0}
dictionaries += 1
for size in range(5):
for letters in product("ab", repeat=size):
text = "".join(letters)
actual = segment_min_cost(text, lexicon)
expected = all_cuts_reference(text, lexicon)
family_ok = family_ok and actual == expected
family_cases += 1
checks["all2511_cases_match_independent_complete_partition_oracle_one_group"] = (
bool(family_ok) and dictionaries == 81 and family_cases == 2511)
order_cases = 0
order_ok = True
for entries in permutations(tuple(cut_lexicon.items())):
order_ok = order_ok and segment_min_cost("abc", dict(entries)) == cut_tie
order_cases += 1
checks["all24_dictionary_insertion_orders_preserve_declared_tie"] = (
bool(order_ok) and order_cases == 24)
class TextSubclass(str):
pass
class DictSubclass(dict):
pass
class IntSubclass(int):
pass
invalids = [
(None, {}), (b"a", {}), (True, {}), (TextSubclass("a"), {}),
("a", []), ("a", (("a", 1),)), ("a", DictSubclass(a=1)),
("a", {None: 1}), ("a", {True: 1}), ("a", {b"a": 1}),
("a", {TextSubclass("a"): 1}), ("a", {"": 1}),
("a", {"a": True}), ("a", {"a": 1.0}), ("a", {"a": "1"}),
("a", {"a": IntSubclass(1)}), ("a", {"a": -1}), ("a", {"a": 10})]
touched = []
def entries_generator():
touched.append(True)
yield ("a", 1)
invalid_ok = all(rejected(text, lexicon) for text, lexicon in invalids)
invalid_ok = invalid_ok and rejected("a", entries_generator()) and touched == []
checks["exact_types_and_cost_ranges_reject_without_coercing_generator"] = bool(invalid_ok)
checks["every_unused_entry_validated_before_empty_or_unreachable_shortcut"] = (
rejected("", {"unused": 0}) and rejected("", {"a": True})
and rejected("z", {"a": 1, "b": -1})
and rejected("a", {"a": 1, "": 2}))
maximum_lexicon = {"aaaa": 9, "0000": 0, "1111": 1, "2222": 2,
"3333": 3, "4444": 4, "5555": 5, "6666": 6}
maximum = segment_min_cost("aaaaaaaa", maximum_lexicon)
checks["inclusive_resource_caps_accept_edges_and_reject_overflow"] = (
maximum["cost"] == 18 and maximum["tokens"] == ("aaaa", "aaaa")
and rejected("a" * 9, {"a": 0})
and rejected("", {str(i): 0 for i in range(9)})
and rejected("", {"aaaaa": 0}))
before = tuple(local_lexicon.items())
exposed = segment_min_cost("abc", local_lexicon)
exposed["tokens"] = ("changed",)
extra = dict(local_lexicon, zz=0)
checks["input_and_new_result_are_independent_with_irrelevant_entry_invariance"] = (
tuple(local_lexicon.items()) == before
and segment_min_cost("abc", local_lexicon) == local
and segment_min_cost("abc", extra) == local
and segment_min_cost("", local_lexicon) == empty)
if len(checks) != 14 or not all(type(v) is bool and v for v in checks.values()):
raise AssertionError(checks)
return {
"runtime": {"implementation": platform.python_implementation(),
"python": platform.python_version(), "dependencies": "stdlib only"},
"contract": {"text_codepoints": [0, MAX_TEXT_POINTS],
"dictionary_entries": [0, MAX_ENTRIES],
"entry_codepoints": [1, MAX_WORD_POINTS], "cost_range": [0, MAX_COST],
"objective": ["minimum total supplied cost", "minimum segment count",
"lexicographically minimum complete end-cut tuple"],
"unit": "raw Python Unicode codepoint offsets; supplied unitless integer costs",
"unknown_word_fallback": False, "normalization": False},
"local_greedy_fixture": {"text": "abc", "dictionary": local_lexicon,
"greedy_tokens": ["a", "bc"], "greedy_cost": 9,
"optimum": local},
"longest_greedy_fixture": {"text": "abcd", "dictionary": longest_lexicon,
"longest_tokens": ["abc", "d"], "longest_cost": 10,
"optimum": longest},
"fewer_segments_tie": count_tie, "end_cut_tie": cut_tie,
"uncovered_suffix": unknown, "empty_text": empty,
"zero_cost": zero, "literal_zwnj": zwnj,
"unicode_fixture": {"text_codepoints": len(emoji), "utf8_bytes": len(emoji.encode("utf-8")),
"result": unicode_result},
"finite_family": {"dictionary_profiles": dictionaries, "texts_per_profile": 31,
"cases_inside_one_group": family_cases},
"dictionary_order_cases_inside_one_group": order_cases,
"invalid_type_cost_cases_inside_one_group": len(invalids) + 1,
"maximum_fixture": maximum, "checks": checks,
"unique_executed_checks": len(checks), "all_passed": True,
"limitations": ["Own finite dictionary tiling, not a Persian tokenizer or language model",
"Supplied additive costs have no measured fluency or search-quality meaning",
"No normalization, grapheme boundary, text repair or unknown-word fallback",
"No Stanford UCS, starter code, NLP package, corpus or external API executed",
"Finite oracle family is evidence for this contract, not all-input testing",
"No timing, memory or production-capacity measurement"]}
if __name__ == "__main__":
data = json.dumps(run_example(), ensure_ascii=True, indent=2, allow_nan=False)
sys.stdout.buffer.write((data + "\n").encode("utf-8"))
مرجعِ مستقل، جدولِ پیشوند ندارد. برای هر زیرمجموعهٔ جایگاههای داخلیِ متن، یک افرازِ کامل میسازد، همهٔ قطعههایش را در واژهنامه بررسی و سپس سهتاییِ هدف را مستقیماً حساب میکند. بهترین افراز با پاسخِ تابع مقایسه میشود. این مسیر از رابطهٔ بازگشتی و نتیجهٔ میانیِ تابع استفاده نمیکند؛ هر افراز را از نو و بهطورِ کامل میسنجد.
چهار مدخلِ a، b، ab و ba هرکدام سه حالتِ غیبت، هزینهٔ صفر یا هزینهٔ دو دارند؛ حاصل هشتادویک واژهنامه است. همهٔ سیویک رشتهٔ دودویی با طولِ صفر تا چهار برای هرکدام بررسی میشوند: ۲۵۱۱ مورد در یک گروهِ مرجع. شاهدهای دستی، تساوی، پوشش، نوعها، سقفها و استقلالِ خروجی گروههای جدا هستند؛ تعدادِ حالتهای حلقه را تعدادِ گروهها نمینامیم.
{
"runtime": {
"implementation": "CPython",
"python": "3.14.5",
"dependencies": "stdlib only"
},
"contract": {
"text_codepoints": [
0,
8
],
"dictionary_entries": [
0,
8
],
"entry_codepoints": [
1,
4
],
"cost_range": [
0,
9
],
"objective": [
"minimum total supplied cost",
"minimum segment count",
"lexicographically minimum complete end-cut tuple"
],
"unit": "raw Python Unicode codepoint offsets; supplied unitless integer costs",
"unknown_word_fallback": false,
"normalization": false
},
"local_greedy_fixture": {
"text": "abc",
"dictionary": {
"a": 0,
"bc": 9,
"ab": 2,
"c": 0
},
"greedy_tokens": [
"a",
"bc"
],
"greedy_cost": 9,
"optimum": {
"status": "complete",
"cost": 2,
"tokens": [
"ab",
"c"
],
"end_cuts": [
2,
3
]
}
},
"longest_greedy_fixture": {
"text": "abcd",
"dictionary": {
"abc": 1,
"d": 9,
"ab": 2,
"cd": 2
},
"longest_tokens": [
"abc",
"d"
],
"longest_cost": 10,
"optimum": {
"status": "complete",
"cost": 4,
"tokens": [
"ab",
"cd"
],
"end_cuts": [
2,
4
]
}
},
"fewer_segments_tie": {
"status": "complete",
"cost": 1,
"tokens": [
"ab"
],
"end_cuts": [
2
]
},
"end_cut_tie": {
"status": "complete",
"cost": 2,
"tokens": [
"a",
"bc"
],
"end_cuts": [
1,
3
]
},
"uncovered_suffix": {
"status": "unsegmentable",
"cost": null,
"tokens": null,
"end_cuts": null
},
"empty_text": {
"status": "complete",
"cost": 0,
"tokens": [],
"end_cuts": []
},
"zero_cost": {
"status": "complete",
"cost": 0,
"tokens": [
"aa",
"aa"
],
"end_cuts": [
2,
4
]
},
"literal_zwnj": {
"status": "complete",
"cost": 2,
"tokens": [
"a",
"b"
],
"end_cuts": [
2,
3
]
},
"unicode_fixture": {
"text_codepoints": 2,
"utf8_bytes": 5,
"result": {
"status": "complete",
"cost": 1,
"tokens": [
"🙂",
"a"
],
"end_cuts": [
1,
2
]
}
},
"finite_family": {
"dictionary_profiles": 81,
"texts_per_profile": 31,
"cases_inside_one_group": 2511
},
"dictionary_order_cases_inside_one_group": 24,
"invalid_type_cost_cases_inside_one_group": 19,
"maximum_fixture": {
"status": "complete",
"cost": 18,
"tokens": [
"aaaa",
"aaaa"
],
"end_cuts": [
4,
8
]
},
"checks": {
"global_cost_beats_locally_cheapest_next_word": true,
"global_cost_beats_longest_next_word": true,
"equal_cost_prefers_fewer_segments": true,
"equal_cost_count_prefers_lexicographic_complete_cut_tuple": true,
"complete_coverage_required_and_empty_success_is_not_failure": true,
"zero_cost_nonempty_segments_advance_and_respect_count_tie": true,
"zwnj_and_spaces_are_literal_not_automatic_boundaries_or_deleted": true,
"codepoint_cuts_are_not_bytes_graphemes_or_canonical_normalization": true,
"all2511_cases_match_independent_complete_partition_oracle_one_group": true,
"all24_dictionary_insertion_orders_preserve_declared_tie": true,
"exact_types_and_cost_ranges_reject_without_coercing_generator": true,
"every_unused_entry_validated_before_empty_or_unreachable_shortcut": true,
"inclusive_resource_caps_accept_edges_and_reject_overflow": true,
"input_and_new_result_are_independent_with_irrelevant_entry_invariance": true
},
"unique_executed_checks": 14,
"all_passed": true,
"limitations": [
"Own finite dictionary tiling, not a Persian tokenizer or language model",
"Supplied additive costs have no measured fluency or search-quality meaning",
"No normalization, grapheme boundary, text repair or unknown-word fallback",
"No Stanford UCS, starter code, NLP package, corpus or external API executed",
"Finite oracle family is evidence for this contract, not all-input testing",
"No timing, memory or production-capacity measurement"
]
}
منفی بودنِ هزینه در قرارداد رد میشود، هرچند بحثِ همهٔ مدلهای ممکن موضوعِ این درس نیست. صفر مجاز است، چون طولِ قطعه همواره مثبت و موقعیت رو به جلو است. سقفِ متن و واژهنامه برای کوچک ماندنِ مثال و مرجع است؛ زمان، حافظه، نرخِ سرویس یا ظرفیتِ محصول اندازهگیری نشدهاند. این خانوادهٔ محدود نیز جایِ بررسیِ همهٔ ورودیهای مجاز را نمیگیرد.
۵. چهار پرسش دربارهٔ هزینه و معنای واژه

پرسش اول: در مثالِ abc چرا کمترین هزینهٔ قدمِ اول جوابِ مطلوب نمیدهد؟ پاسخ: a هزینهٔ صفر دارد، اما bc پس از آن نه میافزاید. مسیرِ ab و c هزینهٔ دو و صفر دارد و جمعش دو است. مقایسه باید روی پاسخِ کامل انجام شود؛ ارزانتر بودنِ یک قطعه، دربارهٔ ادامهٔ باقیمانده کافی نیست.
پرسش دوم: اگر یک قطعه و دو قطعه هزینهٔ برابر داشته باشند، کدام انتخاب میشود؟ پاسخ: تکقطعه، چون تعدادِ کمتر هدفِ دوم است. در برابریِ تعداد نیز پایانهای قطعهها مقایسه میشوند؛ یک و سه پیش از دو و سه است. تغییرِ این ترجیح باید در هدف، جدول و مرجع همزمان اعمال شود.
پرسش سوم: پوششِ ab از abx موفقیت محسوب میشود؟ پاسخ: خیر؛ x باقی مانده و خروجیِ کامل نداریم. وضعیتِ بیپوشش با null گزارش میشود، نه با قطعههای جزئی یا هزینهٔ صفر. به همین دلیل، افزودنِ مدخلِ پوشانندهٔ x میتواند وضعیت را عوض کند، اما افزودنِ مدخلِ نامرتبطِ zz نباید پاسخِ قبلی را تغییر دهد.
پرسش چهارم: آیا این نتیجه یک توکنسازِ فارسی یا تکمیلکنندهٔ پرسش است؟ پاسخ: خیر؛ حتی دو شکلِ ترکیبیِ یک حرف خودکار برابر نمیشوند و برش میتواند وسطِ خوشهٔ نویسهای باشد. تکمیلِ پیشوند با Trie از میانِ مدخلهای آغازشونده پیشنهاد میدهد؛ این درس پوششِ کلِ رشته را با هزینهٔ قراردادی میسنجد. منابع در ۲۹ مهر ۱۴۰۵ خوانده شدند؛ متن آموزشِ مدلسازی است، نه خبرِ پژوهشی یا گزارشِ کیفیتِ محصول.
منابع و مطالعهٔ بیشتر
- Stanford CS221؛ مسئلهٔ کاملِ جداسازی و جمعِ هزینهٔ واژهها
- Python3.14؛ عملیاتِ دنباله، ترتیبِ تاپل و رشتهٔ نقاطِ کد
بررسی منابع: ۲۹ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

