Строка представляет неизменяемую последовательность кодовых единиц UTF-16. Свойство Length сообщает число значений char, а индекс возвращает одну кодовую единицу; ни одно из этих свойств не гарантирует число отображаемых пользователю символов. Любое смысловое изменение текста создаёт новое строковое значение либо выполняется через отдельный изменяемый буфер.

Текстовый алгоритм обязан явно задавать контракт сравнения и разбора. Порядковое сравнение, сравнение без учёта регистра и культурно-зависимое сравнение решают разные задачи; null, пустая строка, строка из пробельных символов и отсутствующее поле также являются разными состояниями. Разделитель, допустимые пустые части, правила кавычек, числовая культура и поведение при первом неверном фрагменте определяются до реализации.

Способ построения результата следует из его формы. char[] подходит, когда длина известна заранее и каждой выходной позиции соответствует одна кодовая единица. StringBuilder используется для последовательного добавления, удаления и замены фрагментов переменной длины. Повторная конкатенация неизменяемых строк внутри цикла может многократно копировать уже построенный префикс и превращать линейную по смыслу операцию в квадратичную.

Опора — Раздел 19: строки и обработка текста. Дополнительно используются строковые литералы и nullable-ссылки из Раздела 4, форматирование и культура из Раздела 8, консольный и числовой разбор из Раздела 11, циклы, инварианты и классы роста из Раздела 12, методы и кортежи из Раздела 13, одномерные и многомерные массивы из Разделов 15 и 17, ссылочная семантика и неизменяемость string из Раздела 16.

Каждая задача оформляется отдельной консольной программой полным шаблоном курса и компилируется с <Nullable>enable</Nullable>. Разрешены только конструкции Разделов 1–19. Для изменяемой сборки текста разрешён System.Text.StringBuilder; правила культуры задаются явно через CultureInfo, StringComparison либо NumberStyles. Регулярные выражения, Span<T>, ReadOnlySpan<T>, Rune, StringInfo, коллекции, LINQ, собственные struct, дополнительные классы, делегаты, лямбда-выражения и обработка исключений запрещены. Алгоритм по индексам строки считается алгоритмом над кодовыми единицами UTF-16 и не объявляется полноценной обработкой графем Unicode. Каждый парсер фиксирует допустимый алфавит, пустые значения, культуру, позицию отказа и состояние выходных данных при неуспехе.


База практических заданий

Уровень I. Базовый

Текстовый контракт, библиотечная операция либо посимвольный алгоритм, границы обхода и способ построения результата заданы условием. Требуется точно реализовать готовую модель, сохранить исходную строку и сверить ручной результат с указанным библиотечным методом или независимым свойством. Ученик не выбирает формат и стратегию: сложность возрастает от классификации состояния строки к поиску, выделению частей, неизменяемым преобразованиям и ручному поиску образца.

1. null, пустая и пробельная строка: Реализовать:

static string Classify(string? text)

Метод возвращает ровно одну категорию:

  1. "null";
  2. "empty";
  3. "whitespace";
  4. "content".

Проверки выполняются именно в этом порядке. Для каждого входа вывести text?.Length, string.IsNullOrEmpty(text) и string.IsNullOrWhiteSpace(text). Объяснить, почему два библиотечных предиката не различают все четыре состояния без дополнительного порядка проверок.

2. Литералы и управляющие последовательности: Представить одно содержимое тремя способами:

  • обычным литералом с \\, \", \t и \n;
  • дословным литералом @"...";
  • интерполированным дословным литералом $@"...".

Текст должен содержать путь C:\data\new, фразу "value" и две строки таблицы name\tcount. Подтвердить оператором ==, что три результата имеют одинаковое содержимое, вывести их длины и отдельно объяснить различие экранирования в обычной и дословной строках.

3. Инвентаризация кодовых единиц: Для заданной строки вывести:

  • Length;
  • индекс каждой позиции;
  • отображаемый char;
  • десятичный код;
  • код в формате X4;
  • признаки char.IsLetter, char.IsDigit и char.IsWhiteSpace.

Выполнить индексированный обход и отдельный foreach, подтвердив одинаковое число посещений. Объяснить, почему foreach не предоставляет координату автоматически и почему явное преобразование char к int возвращает код одной единицы UTF-16.

4. Равенство и правила сравнения: Для каждой пары строк выполнить:

  • ==;
  • string.Equals с StringComparison.Ordinal;
  • string.Equals с StringComparison.OrdinalIgnoreCase;
  • string.Compare без учёта регистра в явно созданной культуре tr-TR.

Дополнительно проверить ReferenceEquals. Использовать пары "File"/"file", "I"/"ı" и две независимо созданные строки "alpha". Разделить равенство содержимого, порядковое правило, культурное правило и идентичность объектов.

5. Первое, последнее и число вхождений символа: Реализовать:

static (
    int first,
    int last,
    int count) FindChar(
        string text,
        char target)

за один индексированный проход. Отсутствие кодируется (-1, -1, 0). Первое вхождение записывается только один раз, последнее обновляется при каждом совпадении. Сверить позиции с IndexOf и LastIndexOf.

6. Ручная обрезка краёв: Реализовать:

static string TrimManual(string text)

Через два индекса найти первую и последнюю непробельные кодовые единицы по char.IsWhiteSpace. Пустая строка и строка только из пробельных символов дают string.Empty; иначе выполняется один Substring(start, length). Вывести вычисленные границы и сверить результат с Trim.

7. Неизменяемые библиотечные преобразования: Для строки " alpha-beta-gamma " последовательно получить и сохранить отдельные результаты:

  • Trim;
  • проверку StartsWith("alpha", StringComparison.Ordinal);
  • проверку EndsWith("gamma", StringComparison.Ordinal);
  • позицию "beta" через IndexOf с StringComparison.Ordinal;
  • Substring для среднего слова;
  • Replace("-", " | ");
  • Remove для удаления среднего слова вместе с одним разделителем;
  • Insert для добавления префикса.

После каждого шага вывести исходную строку и новый результат. Подтвердить, что ни один метод не изменил исходный объект.

8. Разбиение и соединение: Для строки " alpha beta gamma " выполнить два разбиения по символу пробела:

  1. с сохранением пустых частей;
  2. с StringSplitOptions.RemoveEmptyEntries.

Вывести число и содержимое всех элементов, включая пустые, затем соединить непустые части через:

string.Join(" | ", parts)

Отдельно проверить строку "a;;b;" с разделителем ';' без удаления пустых частей и объяснить, почему пустой фрагмент может быть либо шумом повторного разделителя, либо значимым полем формата.

9. Категории и изменение регистра: За один проход по строке подсчитать буквы, цифры, пробельные и прочие кодовые единицы через методы char. Затем получить ToUpperInvariant и ToLowerInvariant, подтвердить сохранность исходного текста и равенство суммы категорий Length. В комментарии указать, что изменение регистра всей строки является библиотечной Unicode-операцией, но индексированный анализ по-прежнему работает по char.

10. Редактирование через StringBuilder: Из исходной строки "alpha beta" создать StringBuilder и выполнить заранее заданную последовательность:

  1. Insert;
  2. Replace;
  3. Remove;
  4. Append;
  5. AppendLine.

После каждого действия вывести Length и текущее содержимое. В конце вызвать ToString и подтвердить, что исходная строка осталась прежней. Все позиции и длины до вызова проверяются условием задачи.

11. Перевёрнутая копия через char[]: Реализовать:

static string ReverseCodeUnits(string text)

Создать char[] длины text.Length, записать в позицию i значение text[text.Length - 1 - i] и вернуть new string(buffer). Сверить с вариантом на StringBuilder, проверить пустую, односимвольную, чётную и нечётную длину. Зафиксировать, что разворачиваются кодовые единицы, а не пользовательские символы.

12. Наивный порядковый поиск подстроки: Реализовать:

static (
    int index,
    long comparisons) IndexOfManual(
        string text,
        string pattern)

Для каждой допустимой стартовой позиции сравнивать образец слева направо и прекращать внутренний цикл при первом несовпадении. Пустой образец находится в позиции 0; образец длиннее текста отсутствует. Вернуть первый индекс либо -1, подсчитать сравнения char и сверить результат с:

text.IndexOf(
    pattern,
    StringComparison.Ordinal)

Уровень II. Продвинутый

Условие задаёт смысл текстового формата или требуемое свойство результата, но не число проходов, состояния разбора, правила пустых частей и представление выходного текста. Требуется самостоятельно спроектировать конвейер распознавания и сборки, выбрать string, char[], массив строк либо StringBuilder, сформулировать инвариант и обеспечить согласованный отказ. В отличие от Уровня I ученик определяет сам текстовый контракт и достаточное состояние алгоритма.

1. Список целых чисел: Спроектировать:

static bool TryParseIntegerList(
    string? text,
    out int[] values,
    out int errorPart)

Числа разделяются произвольными сериями пробельных символов. null и строка без данных дают false, пустой массив и errorPart = -1. Разбор выполняется через Split с удалением пустых частей и int.TryParse с NumberStyles.Integer и InvariantCulture. При первом неверном фрагменте вернуть его индекс; при успехе вычислить сумму long, минимум, максимум и среднее.

2. Запись с обязательными пустыми полями: Дана запись:

surname;score;comment;status

Пустое поле означает отсутствие значения и не удаляется. Спроектировать разбор с точным числом частей, Trim каждого поля и double? score, разбираемым в InvariantCulture. При неверном числе полей либо ошибочном score вернуть согласованный отказ. Нормализованную запись собрать через string.Join, сохранив пустые позиции.

3. Нормализация пробельных серий: Построить текст, в котором:

  • краевые пробельные символы удалены;
  • каждая внутренняя непустая серия пробельных кодовых единиц заменена одним обычным пробелом;
  • порядок остальных символов сохранён.

Реализовать один проход со StringBuilder и независимо получить эталон через Split с удалением пустых частей и string.Join. Сформулировать состояние insideWord и доказать совпадение результатов.

4. Капитализация слов с сохранением позиций: Вход состоит из BMP-букв, цифр, знаков и пробельных кодовых единиц. Построить строку той же длины:

  • первая встретившаяся буква каждого слова переводится через char.ToUpperInvariant;
  • последующие буквы того же слова — через char.ToLowerInvariant;
  • небуквенные символы и все пробелы остаются на исходных позициях.

Выбрать char[] как буфер фиксированной длины и самостоятельно определить состояние hasLetterInCurrentWord. Словом считается максимальная серия непробельных символов; цифра или знак до первой буквы не отменяют возможность капитализировать следующую букву.

5. Ручное порядковое сравнение и сортировка: Реализовать:

static int CompareOrdinalManual(
    string left,
    string right)

Сравнивать общую часть по числовым кодам char; при первом различии вернуть отрицательное или положительное значение, а при равном префиксе меньшей считать более короткую строку. Затем устойчиво отсортировать string[] вставками по этому методу. Сверить знак результата с string.CompareOrdinal для всех сравниваемых пар.

6. Анаграммы латинских букв: Даны две строки из ASCII-латиницы и пробельных символов. Регистр и пробелы не учитываются. Использовать один массив частот длины 26: первая строка увеличивает счётчики, вторая уменьшает. Любой другой символ делает вход недопустимым. Доказать, что нулевой итоговый массив эквивалентен равенству мультимножеств букв.

7. Шифр Цезаря: Реализовать взаимно обратные методы шифрования и расшифрования ASCII-латинских букв с сохранением регистра. Сдвиг k нормализовать в [0, 26); остальные кодовые единицы переносить без изменений. Поскольку каждой входной позиции соответствует одна выходная, использовать char[]. Подтвердить:

Decrypt(Encrypt(text,k),k)=text.Decrypt(Encrypt(text,k),k)=text.

8. Баланс круглых скобок: Одним проходом проверить строку. Глубина увеличивается на '(' и уменьшается на ')'; отрицательная глубина обозначает первую лишнюю закрывающую скобку. Вернуть статус, позицию первого нарушения и конечную глубину. Сформулировать инвариант префикса и объяснить, почему равенство общего числа открывающих и закрывающих скобок не гарантирует правильный порядок.

9. Окно с наибольшим числом цифр: Для строки и целого windowLength найти самый ранний полуинтервал заданной длины с максимальным числом цифровых char. При нулевой длине вернуть пустое окно в позиции 0; длина больше text.Length недопустима. Сначала реализовать прямой алгоритм O(nk), затем скользящее обновление O(n) и подтвердить одинаковые позицию и значение.

10. Наибольший общий префикс: Для непустого массива ненулевых строк найти наибольший общий префикс по Ordinal. Поддерживать только длину кандидата и сокращать её при сравнении с очередной строкой; промежуточные Substring запрещены. При достижении нуля обработку можно завершить. Создать итоговую подстроку ровно один раз.

11. Замена непересекающихся вхождений: Спроектировать:

static string ReplaceAllOrdinal(
    string text,
    string pattern,
    string replacement)

Пустой образец запрещён предусловием. Просматривать текст слева направо: при совпадении образца добавлять replacement и сдвигаться на pattern.Length, иначе добавлять текущий char и сдвигаться на 1. Вхождения не перекрываются и выбираются слева направо. Использовать StringBuilder, сформулировать инвариант уже преобразованного префикса и проверить случай, когда замена содержит сам образец.

12. Выровненный текстовый отчёт: Даны массивы одинаковой длины: идентификаторы, описания и значения decimal. Самостоятельно вычислить ширину каждой колонки по заголовку и данным, затем собрать отчёт одним StringBuilder:

  • текст выравнивается влево;
  • числа — вправо;
  • числа форматируются через F2 в InvariantCulture;
  • длина строки-разделителя выводится из ширин и разделителей столбцов.

Обосновать, почему повторная конкатенация внутри цикла создаёт лишние промежуточные строки.

13. Однозначная запись с кавычками: Спроектировать парсер и сериализатор одной записи с разделителем ';'.

Грамматика:

  • запись состоит из одного или нескольких полей;
  • некавычённое поле не содержит ;, ", \r, \n;
  • кавычённое поле начинается и заканчивается ";
  • внутри кавычённого поля пара "" обозначает одну кавычку;
  • после закрывающей кавычки допустим только ; или конец строки;
  • пустые поля разрешены.

Парсер сначала проверяет запись и определяет число полей, затем создаёт string[] точного размера и декодирует содержимое. Сериализатор всегда заключает каждое поле в кавычки и удваивает внутренние кавычки. Подтвердить round-trip:

Parse(Serialize(fields))=fieldsParse(Serialize(fields))=fields

поэлементно и вернуть позицию первого синтаксического отказа.


Уровень III. Экспертный

Исследуется граница текстовой модели, однозначность контракта или механизм стоимости алгоритма. До сборки и запуска требуется предсказать длину в кодовых единицах, результат сравнения, число проходов, создаваемые промежуточные данные и класс роста; после — объяснить наблюдение через UTF-16, культуру, неизменяемость, контроль диапазона или инвариант автомата и динамической программы.

1. Ручной разбор Int32: Объявить:

enum ParseIntStatus
{
    Success,
    Null,
    Empty,
    SignWithoutDigits,
    InvalidCharacter,
    Overflow
}

Реализовать:

static ParseIntStatus TryParseInt32Manual(
    string? text,
    out int value,
    out int errorIndex)

Контракт допускает необязательный ведущий + или - и не менее одной ASCII-цифры; пробелы запрещены. Промежуточный long использовать нельзя. Для представимости int.MinValue накопление ведётся в отрицательной области:

  • выбрать отрицательный предел int.MinValue либо -int.MaxValue;
  • до умножения проверить accumulator относительно limit / 10;
  • после умножения проверить возможность вычесть очередную цифру.

При отказе value = 0, а errorIndex указывает проблемную позицию либо конец строки. Сравнить успешность и значение с int.TryParse при NumberStyles.AllowLeadingSign и InvariantCulture.

2. Границы char и UTF-16: Исследовать строки:

"A"
"é"
"e\u0301"
"\U0001F600"
"A\U0001F600B"

До запуска предсказать Length, шестнадцатеричные коды каждого char, результат ReverseCodeUnits и порядковое равенство визуально похожих форм. После объяснить:

  • составная запись может занимать две кодовые единицы;
  • символ вне BMP представлен суррогатной парой;
  • разворот по char способен поменять половины пары местами;
  • визуальное сходство не означает одинаковую последовательность UTF-16.

Исправление на уровне графем в задачу не входит.

3. Культура, регистр и несовместимые контракты: Для строк "I", "i", "ı" и "İ" построить таблицу:

  • Ordinal;
  • OrdinalIgnoreCase;
  • сравнение без регистра в InvariantCulture;
  • сравнение без регистра в tr-TR;
  • ToUpper и ToLower в обеих культурах.

До запуска записать прогноз для турецких пар, затем зафиксировать фактический результат. Показать пример, в котором данные нормализованы одной культурой, а сравниваются другой, и объяснить, почему такое смешение нарушает единый контракт идентификатора.

4. Цена построения строки: Построить строку из n одинаковых символов тремя способами:

  1. result += symbol в цикле;
  2. заранее созданным char[];
  3. StringBuilder с начальной ёмкостью n.

Для конкатенации до запуска вывести число кодовых единиц накопленных префиксов, которые приходится переносить:

0+1++(n1)=n(n1)2.0+1+\dots+(n-1) = \frac{n(n-1)}{2}.

Для char[] подсчитать n записей, для StringBuildern вызовов Append, не утверждая точное число внутренних копирований реализации. Сравнить результаты и обосновать O(n^2) против O(n) без измерения времени.

5. Самая длинная подстрока без повторов: Найти самый ранний максимальный полуинтервал строки, внутри которого все значения char различны. Массив длины char.MaxValue + 1 хранит последнюю позицию плюс один. Правый край проходит строку слева направо; левый обновляется только вперёд:

left=max(left,lastSeen[current]).left=\max(left,lastSeen[current]).

Сформулировать инвариант уникальности текущего окна и доказать O(n). Результат относится к кодовым единицам UTF-16.

6. Наивный поиск и Кнут—Моррис—Пратт: Реализовать:

static int[] BuildPrefixFunction(string pattern)

static (
    int index,
    long comparisons) IndexOfKmp(
        string text,
        string pattern)

prefix[i] равен длине наибольшего собственного префикса pattern[0..i], являющегося его суффиксом. Поиск не возвращает индекс текста назад и при несовпадении использует уже вычисленную границу образца. Пустой образец находится в позиции 0.

Сравнить с наивным поиском на обычных и неблагоприятных данных, например:

text    = "aaaaaaaaaaaaaaaaab"
pattern = "aaaaaaaab"

Доказать время O(n + m) и память O(m).

7. Кодек серий и однозначность формата: Для строки из ASCII-латинских букв без цифр реализовать кодирование максимальных серий:

aaabbc → a3b2c1

и обратное восстановление с ручным разбором многозначного положительного счётчика. Грамматика однозначна благодаря запрету цифр во входном алфавите. Декодер отвергает:

  • отсутствующий символ;
  • отсутствующий счётчик;
  • нулевой счётчик;
  • посторонний знак;
  • переполнение числа повторов;
  • переполнение итоговой длины.

Подтвердить round-trip, оценить оба направления как O(n) и привести вход, на котором кодированная строка длиннее исходной.

8. Расстояние Левенштейна: Для двух строк определить минимальное число вставок, удалений и замен одной кодовой единицы, необходимое для превращения первой строки во вторую. Сначала реализовать динамическую таблицу int[,] формы (left.Length + 1) × (right.Length + 1):

di,0=i,d0,j=j,d_{i,0}=i, \qquad d_{0,j}=j, di,j=min{di1,j+1,di,j1+1,di1,j1+cost,d_{i,j} = \min \begin{cases} d_{i-1,j}+1,\\ d_{i,j-1}+1,\\ d_{i-1,j-1}+cost, \end{cases}

где cost = 0 при равных char и 1 иначе.

Затем реализовать версию только с двумя одномерными строками таблицы. Подтвердить одинаковый результат, время O(nm) и память O(nm) против O(m). Объяснить, почему задача сравнивает кодовые единицы, а не графемы или лингвистические символы.


Обязательные контрольные наборы

ЗадачаИсходные данные
I.1null, "", " \t\n", " alpha "
I.2путь C:\data\new, фраза "value", строки name\tcount и alpha\t3
I.3"AЯ7 \tΩ"
I.4"File"/"file", "I"/"ı", две независимо созданные строки "alpha"
I.5"abracadabra", цели 'a' и 'z'; ""
I.6"", " \t\n ", " alpha beta\t"
I.7строка из условия
I.8" alpha beta gamma ", "a;;b;"
I.9"AbЯё 7\t!"
I.10исходная строка "alpha beta" и фиксированная допустимая последовательность операций
I.11"", "A", "abcd", "abcde"
I.12("", ""), ("abc", ""), ("abracadabra", "cada"), ("aaaaa", "aaa"), ("abc", "abcd")
II.1" 10 -3 25 ", "1 x 3", строка из одних пробелов, null
II.2"Ivanov; 91.5 ;; passed", "Petrov;;;pending", запись неверной длины
II.3" alpha\t beta \n gamma ", "", строка из пробелов
II.4" hELLO\tмИР 42!"
II.5["alpha","alphabet","Alpha","","beta","альфа"]
II.6"Dormitory"/"Dirty room", "abc"/"abd", пустая пара
II.7"Attack at Dawn!", k = 0, 3, 29, -3
II.8"", "()", "(a(b)c)", ")(", "(()"
II.9"a1b23c456", windowLength = 0, 1, 4, 10, 11
II.10["flower","flow","flight"], ["same"], ["abc",""], ["dog","racecar","car"]
II.11("aaaaa","aa","b"), ("abcabc","abc","abcx"), ("text","z","q")
II.12идентификаторы ["A-1","LONG-ID","B"], описания ["alpha","extended description","x"], значения [12.5m,3m,1042.75m]
II.13["alpha","a;b","a\"b",""," line "]; корректные и повреждённые записи
III.1null, "", "+", "-0", "2147483647", "-2147483648", "2147483648", "-2147483649", "12a"
III.2строки из условия
III.3все пары из множества ["I","i","ı","İ"]
III.4n = 0, 1, 10, 1000, 10000
III.5"", "abcdef", "aaaa", "abcabcbb", "pwwkew", "A\U0001F600B\U0001F600C"
III.6обычные примеры и неблагоприятная пара из условия; пустой образец
III.7"", "a", "aaabbc", двенадцать 'a', "abcdef"; повреждённые записи "a", "a0", "3a", "a999999999999999999999"
III.8("", ""), ("kitten","sitting"), ("flaw","lawn"), ("abc","abc"), ("","alpha"), строки с суррогатной парой

Итог модуля

Ученик рассматривает string как неизменяемую последовательность кодовых единиц UTF-16, различает null, пустой, пробельный и содержательный текст и безопасно использует Length, индексацию и числовой код char. Правило сравнения задаётся явно как порядковое, безрегистровое либо культурное; поиск, подстроки, разбиение, соединение, изменение регистра и пробельные операции применяются без предположения о мутации исходной строки. Ученик проектирует ручные валидаторы, числовые и разделительные парсеры с контролем диапазона и позиции отказа, выбирает char[] для результата фиксированной длины и StringBuilder для переменной сборки и реализует скользящие окна, КМП, кодек серий и динамическое сравнение строк. Корректность подтверждается инвариантами, round-trip-свойствами, сравнением с библиотечным контрактом и оценкой числа проходов, промежуточных копий, времени и дополнительной памяти.

Покрытие опоры и границы

  • string?, null, пустая и пробельная строка: I.1, II.1–II.2, III.1.
  • Обычные, дословные и интерполированные литералы, управляющие последовательности: I.2.
  • UTF-16, Length, индексация, char и числовой код: I.3, I.11, III.2, III.5–III.8.
  • Равенство содержимого, идентичность, регистр, культура и StringComparison: I.4, II.5, III.3.
  • Поиск символа и подстроки, IndexOf, LastIndexOf, StartsWith, EndsWith: I.5, I.7, I.12, II.11, III.6.
  • Substring, Trim, Remove, Insert, Replace: I.6–I.7, II.3, II.11.
  • Split, сохранение и удаление пустых частей, Join: I.8, II.1–II.3, II.13.
  • Изменение регистра и классификация через char: I.3, I.9, II.4, II.6–II.8.
  • Неизменяемость строки и новые результаты операций: I.7, I.9–I.11, III.4.
  • char[] как буфер фиксированной длины: I.11, II.4, II.7, III.5.
  • StringBuilder, добавление, удаление, замена и отчёт переменной длины: I.10, II.3, II.11–II.13, III.4 и III.7.
  • Ручной разбор, знак, цифры, культура, диапазон и согласованный отказ: II.1–II.2, II.13, III.1 и III.7.
  • Все окна фиксированной длины и скользящее состояние: II.9.
  • Линейные и квадратичные текстовые алгоритмы, КМП и динамическое программирование: I.12, II.9–II.11, III.4–III.8.
  • Round-trip и однозначность текстового формата: II.7, II.13, III.7.
  • Классы O(n), O(n+m), O(n^2) и O(nm), дополнительная память и промежуточные копии: II.9–II.13, III.4–III.8.

За границей: один char не обязан соответствовать одному отображаемому символу. Полная работа с графемными кластерами, нормализацией Unicode, Rune, StringInfo, локализованным поиском и сортировкой, регулярными выражениями, Span<T>, потоковой обработкой больших текстов, кодировками файлов и бинарным представлением текста относится к последующим разделам и курсам. Наблюдаемое равенство или порядок в одной культуре не переносится на другой контракт автоматически.