avkonovalov@bmstu.ru
с ящика
…@student.bmstu.ru
.HOMEWORK
.polynom.c
)Составьте программу polynom.c
, вычисляющую значение полинома
и его производной в заданной точке $x_0$. Коэффициенты полинома и значение $x_0$ — целые числа в диапазоне от $-2^{63}$ до $2^{63} - 1$.
Программа должна считывать из стандартного потока ввода степень полинома $n$, значение $x_0$ и коэффициенты полинома $a_n,a_{n-1},\ldots,a_0$. В стандартный поток вывода нужно вывести значения $P_n (x_0)$ и $P^{\prime}_{n}(x_0)$.
Для вычисления значения полинома нужно использовать схему Горнера:
\[P_n (x) = (...((a_n x + a_{n-1}) x + a_{n-2})x + \ldots + a_1)x +a_0.\]Например, согласно схеме Горнера
\[P_4 (x) = 3x^4 + 2x^3 - 5x^2 + x - 4 = (((3x + 2) x - 5)x + 1)x - 4.\]Для вычисления значения производной полинома необходимо очевидным образом модифицировать схему Горнера.
mulmod.c
)Составьте программу mulmod.c
, вычисляющую выражение
$\left(a\cdot b\right)\textrm{ mod }m$, то есть остаток от деления произведения
чисел $a$ и $b$ на $m$. Известно, что $a$, $b$ и $m$ — натуральные числа,
меньшие, чем $2^{63}$, причем $m \ne 0$.
Программа должна считывать из стандартного потока ввода числа $a$, $b$ и $m$ и выводить результат в стандартный поток вывода.
Основная сложность этой задачи заключается в том, что произведение $a$ на $b$ может превышать $2^{64}$ и, тем самым, не помещаться ни в один из целочисленных типов данных языка C. При этом представление формулы в виде \(\left(\left(a\textrm{ mod }m\right)\cdot\left(b\textrm{ mod }m\right)\right)\textrm{ mod }m\) тоже не решает проблемы, так как при больших значениях $m$ произведение \(\left(a\textrm{ mod }m\right)\cdot\left(b\textrm{ mod }m\right)\) тоже может превышать $2^{64}$.
Решение этой задачи сводится к вычислению значения полинома по схеме Горнера. Представим число $b$ в двоичной системе счисления:
\[b=\overline{b_{63}b_{62}\ldots b_{1}b_{0}}.\]Здесь $b_{63},:b_{62},:\ldots:,:b_{1},:b_{0}$ — двоичные разряды, формирующие число, то есть
\[b=b_{63}\cdot2^{63}+b_{62}\cdot2^{62}+\ldots+b_{1}\cdot2+b_{0}.\]Тогда
\[\left(a\cdot b\right)\textrm{ mod }m=\left[a\cdot b_{63}\cdot2^{63}+a\cdot b_{62}\cdot2^{62}+\ldots+a\cdot b_{1}\cdot2+a\cdot b_{0}\right]\textrm{ mod }m.\]Преобразовав это выражение по схеме Горнера, получим
\[\left(a\cdot b\right)\textrm{ mod }m=\left[\left(\ldots\left(a\cdot b_{63}\cdot2+a\cdot b_{62}\right)\cdot2+\ldots+a\cdot b_{1}\right)\cdot2+a\cdot b_{0}\right]\textrm{ mod }m.\]Учитывая, что для любых $x$, $y$ и $m \ne 0$ справедливы тождества
\(\left(x+y\right)\textrm{ mod }m\equiv\left(\left(x\textrm{ mod }m\right)+\left(y\textrm{ mod }m\right)\right)\textrm{ mod }m,\) \(\left(x\cdot y\right)\textrm{ mod }m\equiv\left(\left(x\textrm{ mod }m\right)\cdot\left(y\textrm{ mod }m\right)\right)\textrm{ mod }m,\)
мы имеем право при вычислении правой части нашей формулы поступать следующим образом: если есть возможность того, что сумма двух слагаемых превзойдёт $2^{64}$, нужно складывать остатки от деления этих слагаемых на $m$; аналогично для произведения. Этот приём даёт гарантию того, что при вычислении ни разу не произойдёт переполнение.
fibsys.c
)Числа Фибоначчи — это последовательность натуральных чисел {$f_{i}$}, в которой
\[\begin{cases}f_{0}=f_{1}=1,\\f_{i}=f_{i-2}+f_{i-1}, & \forall i>1.\end{cases}\]По теореме Цекендорфа любое положительное целое число может быть единственным образом представлено в виде суммы различных чисел Фибоначчи, не включающей двух подряд идущих чисел Фибоначчи.
Например, для числа $100$ суммой Цекендорфа будет $89 + 8 + 3$. Все остальные суммы различных чисел Фибоначчи, равные $100$, содержат два подряд идущих числа Фибоначчи.
Чтобы разложить целое положительное число $x$ в сумму Цекендорфа, нужно до тех пор, пока $x > 0$, выполнять следующие действия:
Напомним, что в позиционной системе счисления вес каждой цифры зависит от её позиции в записи числа. Соответственно, базисом позиционной системы счисления называется последовательность чисел, задающих вес каждого разряда. Например, для десятичной системы базисом является последовательность 1, 10, 100, 1000, 10000, 100000, и т.д.
Фибоначчиева система счисления — это позиционная система счисления с двумя цифрами — $0$ и $1$, базисом которой являются числа Фибоначчи, начиная с $f_1$: 1, 2, 3, 5, 8, 13, 21, 34, и т.д.
Для того чтобы гарантировать единственность записи числа в фибоначчиевой системе счисления, запись числа не должна содержать две подряд идущие единицы (теорема Цекендорфа).
Составьте программу fibsys.c
, выполняющую перевод целого числа $0\leq x<2^{63}$
в фибоначчиеву систему счисления.
Программа должна считывать из стандартного потока ввода число $x$ и выводить в стандартный поток вывода последовательность нулей и единиц, образующую запись числа $x$ в фибоначчиевой системе счисления.
intersect.c
)Пусть $\mathbb{N}_{32}$ — множество натуральных чисел от 0 до 31. Даны два
множества $A\subseteq\mathbb{N}_{32}$ и $B\subseteq\mathbb{N}_{32}$. Составьте
программу intersect.c
, вычисляющую пересечение множеств $A$ и $B$.
Программа должна считывать из стандартного потока ввода размер множества $A$ и элементы множества $A$, а затем — размер множества $B$ и элементы множества $B$. Программа должна выводить в стандартный поток вывода элементы множества $A\cap B$, отсортированные в порядке возрастания.
Использовать массивы для хранения множеств запрещается: каждое множество должно быть представлено 32-разрядным целым числом таким, что если его $i$-й бит равен 1, то число $i$ принадлежит множеству.
permut.c
)Даны два целочисленных массива размером $8$ элементов. Составьте программу
permut.c
, определяющую, можно ли получить один массив перестановкой элементов
другого массива.
Программа должна считывать из стандартного потока ввода элементы обоих массивов,
а затем выводить в стандартный поток вывода слово «yes
», если массивы
совпадают с точностью до перестановки элементов, и «no
» — в противном случае.
Сортировать массивы запрещается.
maxk.c
)Дан целочисленный массив, размер которого не превышает $1\,000\,000$, и число $k$,
которое меньше или равно длине массива. Составьте программу maxk.c
,
определяющую, какие $k$ подряд идущих элементов массива имеют максимальную сумму.
Программа должна считывать из стандартного потока ввода размер массива, его элементы и число $k$, а затем выводить в стандартный поток вывода максимальную сумму $k$ подряд идущих элементов массива.
В программе запрещается обращаться к одному и тому же элементу массива более двух раз, а также объявлять какие бы то ни было вспомогательные массивы.
primediv.c
)Составьте программу primediv.c
, вычисляющую наибольший простой делитель
некоторого числа $x$. Число $x$ вводится со стандартного потока ввода, причём
известно, что $-2^{31} \le x < 2^{31}$.
Программа должна использовать решето Эратосфена для построения массива простых чисел.
saddlepoint.c
)Элемент двумерного массива называется седловым, если он одновременно наибольший в своей строке и наименьший в своём столбце.
Дан целочисленный двумерный массив, размер которого не превышает $10 \times 10$.
Известно, что все элементы массива различны. Составьте программу
saddlepoint.c
, определяющую седловую точку в этом массиве.
Программа должна считывать из стандартного потока ввода количество строк
и столбцов двумерного массива и его элементы. Программа должна вывести
в стандартный поток координаты найденной седловой точки или слово «none
»,
если седловой точки не существует.
В программе запрещается обращаться к одному и тому же элементу массива дважды.
revarray.c
)Составьте функцию revarray
, переставляющую элементы любого массива в обратном
порядке. Функция должна быть объявлена как
void revarray(void *base, size_t nel, size_t width)
{
...
}
Здесь параметр base
означает указатель на начало массива, nel
— количество
элементов в массиве, а width
— размер каждого элемента массива в байтах.
Примечание. Тип size_t
определён в заголовочных файлах <stddef.h>
,
<stdio.h>
, <stdlib.h>
, <string.h>
, <time.h>
и <wchar.h>
.
maxarray.c
)Составьте функцию maxarray
, возвращающую индекс максимального элемента
произвольного массива. Функция должна быть объявлена как
int maxarray(void *base, size_t nel, size_t width,
int (*compare)(void *a, void *b))
{
...
}
Здесь параметр base
означает указатель на начало массива, nel
— количество
элементов в массиве, width
— размер каждого элемента массива в байтах,
а compare
— указатель на функцию сравнения двух элементов, работающую
аналогично функции сравнения для библиотечной функции qsort
.
Примечание. Тип size_t
определён в заголовочных файлах <stddef.h>
,
<stdio.h>
, <stdlib.h>
, <string.h>
, <time.h>
и <wchar.h>
.
binsearch.c
)Составьте функцию binsearch
, выполняющую поиск заданного числа
в последовательности чисел, отсортированной по возрастанию, методом деления
пополам. Функция должна быть объявлена как
unsigned long binsearch(unsigned long nel, int (*compare)(unsigned long i))
{
...
}
Здесь параметр nel
задаёт количество элементов в последовательности,
а параметр compare
— указатель на функцию сравнения, которая принимает
параметр i
и и возвращает:
i
-тое число в последовательности меньше искомого числа;i
-тое число больше искомого числа.Функция binsearch
должна возвращать индекс найденного элемента или значение
nel
, если такого элемента не существует.
peak.c
)Элемент последовательности чисел, значение которого — не меньше значений его непосредственных соседей, называется пиком. Очевидно, что непустая последовательность размера $n$ имеет от $1$ до $n$ пиков.
Составьте функцию peak
, возвращающую индекс любого пика в последовательности.
Функция должна быть объявлена как
unsigned long peak(unsigned long nel,
int (*less)(unsigned long i, unsigned long j))
{
...
}
Здесь параметр nel
задаёт количество элементов в последовательности,
а параметр less
— указатель на функцию сравнения, которая принимает два
параметра — i
и j
— и возвращает $1$, если i
-тое число
в последовательности меньше j
-того числа, и $0$ — в противном случае.
concat.c
)Составьте функцию concat
, выполняющую конкатенацию произвольного количества
строк:
char *concat(char **s, int n)
{
...
}
Здесь s
— указатель на массив соединяемых строк, n
— количество строк
в массиве. Функция должна создавать в динамической памяти новую строку, размер
которой равен суммарному размеру всех соединяемых строк, записывать в неё
соединяемые строки друг за другом в том порядке, в котором они перечислены
в массиве, и возвращать указатель на новую строку.
Программа concat.c
, демонстрирующая работоспособность функции concat
,
должна считывать со стандартного потока ввода количество строк и сами
соединяемые строки и выводить в стандартный поток вывода результирующую строку.
В автотестах длины строк, подаваемых на стандартный ввод, не превышают $1000$
символов. Однако, аргументы функции concat()
могут иметь произвольную длину.
wcount.c
)Составьте функцию wcount
, вычисляющую количество слов в строке. Слово — это
подстрока, не содержащая пробелов. Слова разделяются произвольным количеством
пробелов. Кроме того, строка может начинаться и заканчиваться произвольным
количеством пробелов. Объявление функции должно выглядеть как
int wcount(char *s)
{
...
}
Итоговую программу, содержащую как функцию wcount
, так и функцию main
,
демонстрирующую работоспособность функции wcount
, нужно назвать wcount.c
.
Строка должна считываться из стандартного потока ввода с помощью функции gets
или fgets
, минимальный размер буфера — $1000$ символов, включая концевой нуль.
Примечание. Функция fgets
, в отличие от gets
, добавляет \n
в конец
прочитанной строки.
fibstr.c
)Строки Фибоначчи — это последовательность строк ${s_i}$, составвленных из букв $a$ и $b$, в которой
\[\begin{cases}s_{1}=a,\\s_{2}=b,\\s_{i}=s_{i-2}s_{i-1}, & \forall i>2.\end{cases}\]Чтобы было понятнее, приведём первые пять строк Фибоначчи: $a$, $b$, $ab$, $bab$, $abbab$.
Составьте функцию fibstr
, возвращающую $n$-ную строку Фибоначчи. Функция
fibstr
должна быть объявлена как
char *fibstr(int n)
{
...
}
Функция fibstr
должна выделять в динамической памяти массив такого размера,
чтобы в него как раз поместилась $n$-ная строка Фибоначчи. Ответственность
за освобождение памяти, занятой строкой, лежит на функции, вызывающей функцию
fibstr
.
Составьте программу fibstr.c
, демонстрирующую работоспособность составленной
функции.
strdiff.c
)Составьте функцию strdiff
, выполняющую побитовое сравнение двух строк.
Функция должна быть объявлена как
int strdiff(char *a, char *b)
{
...
}
Если строки равны, функция должна возвращать $-1$. В противном случае, она должна возвращать порядковый номер бита, до которого содержимое строк совпадает.
Например, строки aa и ai представлены следующими последовательностями битов (последовательности записаны справа налево, то есть младший бит — самый правый):
\[\begin{array}{ccc}00000000 & \underbrace{01100001} & \underbrace{01100001}\\ & a & a\\ 00000000 & \underbrace{01101001} & \underbrace{01100001}\\ & i & a\end{array}\]Эти строки совпадают до $11$ бита (биты нумеруются, начиная с $0$).
frame.c
)Составьте программу frame.c
, выполняющую рисование рамки вокруг текстовой
строки. Программа должна принимать в качестве аргументов командной строки
размеры рамки и значение строки.
Например, пусть программа вызвана как
./frame 6 20 Abracadabra
Тогда в стандартный поток вывода должно быть выведено
********************
* *
* Abracadabra *
* *
* *
********************
Текстовая строка должна быть отцентрирована как по горизонтали, так
и по вертикали. В случае, если длина строки не позволяет вписать строку в рамку
заданного размера, программа должна вместо рамки выводить сообщение Error
.
Если программа вызвана с неправильным количеством аргументов командной строки, необходимо вывести подсказку
Usage: frame <height> <width> <text>
Из-за ограничений системы автоматического тестирования код возврата программы
всегда должен быть равен 0
, даже при ошибочных данных.
searchlist.c
)Пусть в гипотетическом интерпретаторе языка Lisp элемент списка представлен
в памяти структурой Elem
, которая объявлена в заголовочном файле
elem.h
следующим образом:
#ifndef ELEM_H_INCLUDED
#define ELEM_H_INCLUDED
struct Elem {
/* «Тег», описывающий тип значения в «головe» списка */
enum {
INTEGER,
FLOAT,
LIST
} tag;
/* Само значение в «голове» списка */
union {
int i;
float f;
struct Elem *list;
} value;
/* Указатель на «хвост» списка */
struct Elem *tail;
};
#endif
Таким образом, в качестве полезной нагрузки в элементе списка может храниться
целое число, число с плавающей точкой или указатель на список. Причём тип
хранимого значения определяется полем tag
.
Кроме того, в элементе списка хранится указатель tail
на «хвост» списка.
Если элемент является последним в списке, этот указатель принимает значение
NULL
.
Составьте функцию searchlist
, выполняющую поиск элемента списка, содержащего
указанное целое число:
struct Elem *searchlist(struct Elem *list, int k)
{
...
}
Здесь параметр list
означает указатель на первый элемент списка, k
— искомое
целое число.
Функция searchlist
должна возвращать указатель на найденный элемент списка
или NULL
, если элемент, содержащий число k
, не найден.
Указание. Добавьте в файл с решением #include "elem.h"
(он есть в среде
тестирования) или целиком определение структуры struct Elem
, скопировав её
из условия задачи. Скачать файл: elem.h
.
superstr.c
)Пусть дано множество из $n$ строк, где $0 < n \le 10$. Известно, что ни одна
из этих строк не является подстрокой другой строки. Составьте программу
superstr.c
, вычисляющую длину кратчайшей строки, содержащей все эти строки
в качестве подстрок.
Программа должна считывать из стандартного потока ввода число $n$, а затем $n$ строк. Длина кратчайшей строки, содержащей все $n$ считанных строк, должна выводиться в стандартный поток вывода.
power2.c
)Пусть дана последовательность из $n$ неповторяющихся целых чисел, где $0 < n \le 24$, и каждое целое число находится в диапазоне от $-10^6$ до $10^6$.
Составьте программу power2.c
, вычисляющую, сколько существует непустых
сочетаний чисел из последовательности таких, что сумма чисел в сочетании равна
степени числа $2$.
Программа должна считывать из стандартного потока ввода число $n$, а затем $n$ чисел, образующих последовательность. Программа должна выводить количество сочетаний в стандартный поток вывода.
bubblesort.c
)В классической сортировке пузырьком проход по сортируемой последовательности осуществляется всегда в одном направлении. Модифицируйте алгоритм сортировки пузырьком, чтобы в нём чередовались проходы по последовательности слева направо и справа налево.
Составьте функцию bubblesort
, осуществляющую двунаправленную пузырьковую
сортировку произвольной последовательности. Функция должна быть объявлена как
void bubblesort(unsigned long nel,
int (*compare)(unsigned long i, unsigned long j),
void (*swap)(unsigned long i, unsigned long j))
{
...
}
Параметры функции bubblesort
:
nel
— количество элементов в последовательности;compare
— указатель на функцию сравнения, которая возвращает $-1$, если
$i$-й элемент меньше $j$-го, $0$ — в случае, если $i$-й элемент равен $j$-му,
и $1$ — в случае, если $i$-й элемент больше $j$-го;swap
— указатель на функцию обмена $i$-го и $j$-го элементов
последовательности.shellsort.c
)В классической сортировке втавками для вставки элемента в отсортированную часть последовательности выполняется сравнение элемента со всеми членами отсортированной части до тех пор, пока для него не будет найдено место, то есть переменная $loc$ (см. алгоритм в лекциях) на каждой итерции внутреннего цикла уменьшается на единицу.
Метод Шелла является модификацией сортировки вставками, в которой переменная $loc$ на каждой итерции внутреннего цикла уменьшается на некоторое число $d \ge 1$. При этом фактически сортировка выполняется несколько раз для всё меньших и меньших значений $d$ до тех пор, пока $d$ не станет равно $1$. Тем самым, сначала выполняется серия «грубых» сортировок, которые не дают точного ответа, но делают последовательность более упорядоченной, обеспечивая более быстрое выполнение финальной точной сортировки при $d = 1$.
Составьте функцию shellsort
, выполняющую сортировку произвольной
последовательности методом Шелла. Функция shellsort
должна быть объявлена как
void shellsort(unsigned long nel,
int (*compare)(unsigned long i, unsigned long j),
void (*swap)(unsigned long i, unsigned long j))
{
...
}
Параметры функции shellsort
:
nel
— количество элементов в последовательности;compare
— указатель на функцию сравнения, которая возвращает $-1$, если
$i$-й элемент меньше $j$-го, $0$ — в случае, если $i$-й элемент равен $j$-му,
и $1$ — в случае, если $i$-й элемент больше $j$-го;swap
— указатель на функцию обмена $i$-то и $j$-го элементов
последовательности.Значения расстояния $d$ в ходе работы функции должны образовывать
последовательность Фибоначчи (естественно, записанную задом наперёд). Первое
значение в этой последовательности должно быть максимальным числом Фибоначчи,
которое меньше значения параметра nel
.
csort.c
)Составьте функцию сsort
, выполняющую сортировку слов в предложении методом
подсчёта сравнений. Слова в предложении разделяются произвольным количеством
пробелов. Функция csort
должна быть объявлена следующим образом:
void csort(char *src, char *dest)
{
...
}
В качестве параметров функция csort
принимает указатель на исходное
предложение src
и указатель на пустой буфер dest
подходящего размера.
В результате работы функции в буфер dest
записывается новое предложение,
состоящее из слов, взятых из исходного предложения и отсортированных в порядке
возрастания их длин. При этом слова в новом предложении разделяются одним
пробелом.
Рассмотрим пример работы функции csort
. Пусть исходное предложение выглядит
как
qqq www t aa rrr bb x y zz
Тогда в выходной буфер должно быть записано предложение
t x y aa bb zz qqq www rrr
Итоговую программу, содержащую как функцию csort
, так и функцию main
,
демонстрирующую работоспособность функции csort
, нужно назвать csort.c
.
Программа должна считывать исходное предложение со стандартного потока ввода.
Минимальный размер буфера для ввода с консоли должен быть равен
$1000$ символов включая \0
в конце строки.
heapsort.c
)Составьте функцию hsort
, выполняющую пирамидальную сортировку произвольного
массива. Объявление функции hsort
должно быть выполнено по аналогии
с функцией qsort
:
void hsort(void *base, size_t nel, size_t width,
int (*compare)(const void *a, const void *b))
{
...
}
В качестве параметров функция hsort
принимает указатель на начало массива
base
, количество элементов массива nel
, размер одного элемента width
и указатель на функцию сравнения compare
.
Замечание. Функция hsort
должна иметь тот же интерфейс, что и функция
qsort
. А функция qsort
предполагает, что функция compare
возвращает
отрицательное значение, когда $a < b$, ноль, когда $a = b$, и положительное
значение, когда $a > b$. Т.е. функция compare
не обязательно возвращает
только -1
, 0
или +1
. См., например, здесь:
Итоговая программа heapsort.c
должна сортировать массив строк в порядке
возрастания количества букв a
в строке. Программа должна считывать
из стандартного потока ввода размер и элементы массива, и выводить
в стандартный поток вывода результат сортировки.
Минимальный размер буфера для ввода с консоли должен быть равен
$1000$ символов включая \0
в конце строки.
mergesort.c
)Составьте программу mergesort.c
, осуществляющую сортировку массива целых
чисел в порядке возрастания модуля числа.
В программе должен быть реализован алгоритм сортировки слиянием, рекурсивную функцию которого нужно модифицировать таким образом, чтобы для последовательностей длиной меньше $5$ выполнялась сортировка вставками.
Размер и элементы массива должны считываться программой из стандартного потока ввода. Результат сортировки должен быть выведен в стандартный поток вывода.
quicksort.c
)Составьте программу quicksort.c
, осуществляющую сортировку массива целых
чисел в порядке возрастания.
В программе должен быть реализован алгоритм быстрой сортировки, рекурсивную функцию которого нужно модифицировать таким образом, чтобы, во-первых, для последовательностей длиной меньше $m$ выполнялась сортировка прямым выбором, а во-вторых, глубина стека вызовов была равна $O\left(\lg n\right)$, где $n$ — размер массива.
Программа должна считывать со стандартного потока ввода размер массива $n$, число $m$ и значения элементов массива. В стандартный поток вывода должны выводиться элементы отсортированного массива.
dsort.c
)Составьте программу dsort.c
, осуществляющую сортировку латинских букв в строке
в алфавитном порядке (размер строки — до миллиона букв). В программе должен быть
реализован алгоритм сортировки распределением.
Например, если введена строка
encyclopedia
то программа должна выводить в стандартный поток вывода
accdeeilnopy
Строка вводится со стандартного потока ввода, причём известно, что она содержит только маленькие латинские буквы.
datesort.c
)Составьте программу datesort.c
, осуществляющую сортировку последовательности
дат по возрастанию. В программе должен быть реализован алгоритм поразрядной
сортировки, адаптированный для случая, когда ключи представляются в системе
счисления с основаниями, зависящими от разряда.
В программе сортируемая последовательность должна быть представлена в виде
массива структур Date
:
struct Date {
int Day, Month, Year;
};
Поле Day
может принимать значения от $1$ до $31$, поле Month
— от $1$
до $12$, а поле Year
— от $1970$ до $2030$.
Последовательность дат считывается из стандартного потока ввода. При этом
в самом начале считывается общее количество дат, а каждая дата представляется
тройкой чисел «yyyy mm dd
».
Например, если введена последовательность
5
2005 01 12
1977 02 01
1994 03 01
2004 02 29
1977 08 01
то программа должна выводить в стандартный поток вывода
1977 02 01
1977 08 01
1994 03 01
2004 02 29
2005 01 12
Указание. Формат вывода для printf()
: "%04d %02d %02d"
.
radixsort.c
)Составьте программу radixsort.c
, осуществляющую сортировку последовательности
$32$-разрядных целых чисел по возрастанию. В программе должен быть реализован
алгоритм поразрядной сортировки.
Программа должна считывать из стандартного потока ввода размер и элементы последовательности, и записывать в стандартный поток вывода элементы отсортированной последовательности.
Например, если на вход программы подано
5
1000 700 -5000 2038 0
то программа должна выводить в стандартный поток вывода
-5000 0 700 1000 2038
В программе сортируемая последовательность должна быть представлена в виде
массива объединений Int32
:
union Int32 {
int x;
unsigned char bytes[4];
};
Тем самым подразумевается, что целые числа представлены в системе счисления
по основанию $256$. Доступ к отдельным байтам целого числа должен осуществляться
через поле bytes
объединения.
prefixes.c
)Составьте программу prefixes.c
, выполняющую поиск всех периодических префиксов
заданной строки $S$. Префикс является периодическим, если его можно представить
в виде
где $d$ — некоторая подстрока. Для поиска префиксов программа должна строить префиксную функцию для строки $S$.
Программа получает строку $S$ через аргументы командной строки, и для каждого найденного префикса выводит в стандартный поток вывода два числа: длину префикса $n$ и количество повторений $k$ подстроки $d$ в префиксе.
Например, пусть программа вызвана как
./prefixes aabaabaabaab
Тогда программа должна выводить в стандартный поток вывода
2 2
6 2
9 3
12 4
Уточнение. Если один и тот же префикс можно разложить на одинаковые подстроки разными способами, то величина $k$ должна быть наибольшей, и, соответственно, $d$ — кратчайшей. Поэтому в примере выше не печатается пара чисел
12 2
kmpall.c
)Составьте программу kmpall.c
, осуществляющую поиск всех вхождений подстроки
$S$ в строку $T$. В программе должен быть реализован алгоритм
Кнута–Морриса–Пратта, изменённый таким образом, чтобы при нахождении очередного
вхождения $S$ в $T$ алгоритм не завершался, а продолжал сканировать строку $T$.
Строки $S$ и $T$ должны передаваться в программу через аргументы командной строки. Программа должна выводить в стандартный поток вывода индексы первых символов всех вхождений $S$ в $T$.
pword.c
)Составьте программу pword.c
, определяющую, составлена ли строка $T$
исключительно из префиксов строки $S$.
Программа получает строки $S$ и $T$ через аргументы командной строки и выводит
«yes
», если $T$ составлена из префиксов $S$, и «no
» — в противном случае.
Например, пусть программа вызвана как
./pword abracadabra abrabracada
Тогда программа должна выводить в стандартный поток вывода «yes
».
bmall.c
)Составьте программу bmall.c
, осуществляющую поиск всех вхождений подстроки $S$
в строку $T$. В программе должен быть реализован алгоритм Бойера–Мура,
изменённый таким образом, чтобы при нахождении очередного вхождения $S$ в $T$
алгоритм не завершался, а продолжал сканировать строку $T$.
Строки $S$ и $T$ должны передаваться в программу через аргументы командной строки. Программа должна выводить в стандартный поток вывода индексы первых символов всех вхождений $S$ в $T$.
Строки $S$ и $T$ могут состоять из произвольных печатных знаков (коды 33…126).
extstop.c
)Существует модификация алгоритма Бойера–Мура, в которой эвристика стоп-символа расширена следующим образом:
Расширенная эвристика стоп-символа. Встретив в строке $T$ символ $x = T[k]$ такой, что $x \neq S[i]$, мы можем расположить строку $S$ относительно строки $T$ так, что последнее вхождение $x$ в $S$, расположенное левее $S[i]$, окажется напротив $T[k]$.
Пример. ($T[2] = a$ — стоп-символ)
\[\begin{array}{ccccccccccccc} & & \mathit{0} & \mathit{1} & \mathit{2} & \mathit{3} & \mathit{4} & \mathit{5} & \mathit{6} & \mathit{7} & \mathit{8} & \mathit{9} & \ldots\\ T= & & a & b & \underline{a} & a & b & a & b & a & c & b & \ldots\\ S= & & c & a & \left\langle \mathbf{b}\right\rangle & \mathbf{a} & \mathbf{b}\\ & & & c & \underline{a} & b & a & b\\ & & & & & & . & . & . \end{array}\]Таблица $\delta_1$ для эффективной реализации расширенной эвристики стоп-символа должна представлять собой матрицу размера $len(S) \times size$, где $size$ — размер алфавита. При неудачном сравнении символов $S[i]$ и $T[k]$ алгоритм Бойера–Мура должен прочитать смещение для переменной $k$ из $\delta_{1}[i,T[k]]$.
Составьте программу extstop.c
, осуществляющую поиск первого вхождения
подстроки $S$ в строку $T$. В программе должен быть реализован вариант алгоритма
Бойера–Мура, в котором не используется эвристика совпавшего суффикса,
а эвристика стоп-символа расширена приведённым выше способом.
Строки $S$ и $T$ должны передаваться в программу через аргументы командной строки. Программа должна вывести в стандартный поток вывода индекс первого символа первого вхождения $S$ в $T$. Если такого вхождения нет, программа должна вывести $len(T)$.
Строки $S$ и $T$ могут состоять из произвольных печатных знаков (коды 33…126).
rangemax.c
)Составьте программу rangemax.c
, выполняющую поиск максимального элемента
на различных интервалах последовательности целых чисел, которая время
от времени изменяется.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит размер последовательности $n$ $(0 < n \le 1\,000\,000)$. Во второй строке перечислены элементы последовательности. Каждый элемент представляет собой целое число, находящееся в диапазоне от $-10^9$ до $10^9$. Элементы разделяются пробелами.
Третья строка содержит общее количество выполняемых операций $m$ $(0 < m \le 20000)$. Каждая из следующих $m$ строк содержит описание операции.
Операция либо имеет форму MAX
$l$ $r$ (найти максимальный элемент
подпоследовательности, начинающейся с элемента с индексом $l$ и заканчивающейся
элементом с индексом $r$), либо форму UPD
$i$ $v$ (присвоить значение $v$
элементу с индексом $i$).
Формат результата работы программы. Для каждой операции MAX
вывести
в стандартный поток вывода значение максимального элемента указанной
подпоследовательности.
rangehd.c
)Гипердром — это строка, из букв которой можно составить палиндром. Другими словами, любая буква имеет чётное количество вхождений (возможно, нулевое) в гипердром чётной длины. Если же гипердром имеет нечётную длину, то ровно одна буква имеет нечётное количество вхождений.
Составьте программу rangehd.c
, определяющую, является ли указанная подстрока
строки гипердромом. Строка время от времени может изменяться.
Формат входных данных. Первая строчка, считываемая со стандартного потока ввода, содержит строку размера $n$ $(0 < n \le 1\,000\,000)$. Строка состоит из маленьких латинских букв.
Вторая строчка содержит общее количество выполняемых операций $m$ $(0 < m \le 10000)$. Каждая из следующих $m$ строчек содержит описание операции.
Операция либо имеет форму HD
$l$ $r$ (определить, является ли подстрока,
начинающаяся с индекса $l$ и заканчивающаяся индексом $r$, гипердромом),
либо форму UPD
$i$ $s$ (заменить подстроку, начинающуюся с индекса $i$,
строкой $s$).
Формат результата работы программы. Для каждой операции HD
вывести
в стандартный поток вывода слово «YES
», если подстрока является гипердромом,
или слово «NO
» в противном случае.
rangepeak.c
)Напомним, что элемент последовательности чисел, значение которого — не меньше значений его непосредственных соседей, называется пиком.
Составьте программу rangepeak.c
, выполняющую вычисление количества пиков
на различных интервалах последовательности целых чисел, которая время
от времени изменяется.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит размер последовательности $n$ $(0 < n \le 1\,000\,000)$. Во второй строке перечислены элементы последовательности. Каждый элемент представляет собой целое число, находящееся в диапазоне от $-10^9$ до $10^9$. Элементы разделяются пробелами.
Третья строка содержит общее количество выполняемых операций $m$ $(0 < m \le 20000)$. Каждая из следующих $m$ строк содержит описание операции.
Операция либо имеет форму PEAK
$l$ $r$ (вычислить количество пиков
в подпоследовательности, начинающейся с элемента с индексом $l$
и заканчивающейся элементом с индексом $r$), либо форму UPD
$i$ $v$
(присвоить значение $v$ элементу с индексом $i$).
Формат результата работы программы. Для каждой операции PEAK
вывести
в стандартный поток вывода количество пиков в указанной подпоследовательности.
rangegcd.c
)Составьте программу rangegcd.c
, вычисляющую наибольший общий делитель
на различных интервалах последовательности целых чисел.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит размер последовательности $n$ $(0 < n \le 300\,000)$. Во второй строке перечислены элементы последовательности. Каждый элемент представляет собой целое число, находящееся в диапазоне от $-10^9$ до $10^9$. Элементы разделяются пробелами.
Третья строка содержит общее количество запросов $m$ $(0 < m \le 1\,000\,000)$. Каждая из следующих $m$ строк содержит запрос, который представляет собой два числа $l$ и $r$, задающие границы интервала, на котором нужно вычислить наименьший общий делитель $(0 \le l,r < n)$.
Формат результата работы программы. Для каждого запроса вывести в стандартный поток вывода наименьший общий делитель указанной подпоследовательности.
maxprod.c
)Составьте программу maxprod.c
, выполняющую поиск отрезка последовательности
простых дробей ${v_i}_0^{n-1}$, на котором произведение дробей максимально.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит размер последовательности $n$ $(0 < n \le 1\,000\,000)$. Во второй строке перечислены элементы последовательности. Каждый элемент записывается в виде $a∕b$, где $a$ и $b$ — неотрицательные целые числа $(0 \le a \le 1\,000\,000, 0 < b \le 1\,000\,000)$. Элементы разделяются пробелами.
Формат результата работы программы. Программа должна вывести в стандартный поток вывода два числа $l$ и $r$ такие, что произведение $\prod_{i=l}^r v_i$ — максимально. Если возможно несколько решений, следует выбрать решение с минимальным $l$.
qsstack.c
)Необходимо составить программу qsstack.c
, осуществляющую сортировку массива
целых чисел в порядке возрастания.
В программе должен быть реализован нерекурсивный алгоритм быстрой сортировки, использующий в своей работе стек заданий. Каждое задание описывает координаты подмассива, который нужно отсортировать, и представляет собой структуру
struct Task {
int low, high;
};
Программа должна считывать со стандартного потока ввода размер массива $n$ и значения элементов массива. В стандартный поток вывода должны выводиться элементы отсортированного массива.
stackmachine.c
)Пусть стековая машина — это устройство для выполнения арифметических операций, использующее для хранения промежуточных результатов вычислений стек целых чисел. Подразумевается, что каждая операция берёт операнды из стека и оставляет на стеке результат.
Составьте программу stackmachine.c
, моделирующую работу стековой машины.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит общее количество выполняемых операций $n$ $(0 < n \le 100\,000)$. Каждая из следующих $n$ строк содержит описание операции.
Стековая машина должна обеспечивать выполнение следующих операций:
CONST
$x$ — кладёт в стек число $x$ $(-1\,000\,000\,000 < x <
1\,000\,000\,000)$;ADD
— сложение (снимает со стека два операнда $a$ и $b$ и кладёт в стек их
сумму);SUB
— вычитание (снимает со стека операнд $a$, затем снимает со стека
операнд $b$, кладёт в стек $a - b$);MUL
— умножение (снимает со стека два операнда $a$ и $b$ и кладёт в стек их
произведение);DIV
— деление (снимает со стека операнд $a$, затем снимает со стека операнд
$b$, кладёт в стек результат целочисленного деления $a$ на $b$);MAX
— максимум двух чисел (снимает со стека два операнда $a$ и $b$ и кладёт
в стек $\max(a,b)$);MIN
— минимум двух чисел (снимает со стека два операнда $a$ и $b$ и кладёт
в стек $\min(a,b)$);NEG
— меняет знак числа, находящегося на вершине стека;DUP
— кладёт в стек копию числа, находящегося на вершине стека;SWAP
— меняет местами два числа, находящиеся на вершине стека.Можно считать, что последовательность операций составлена правильно, то есть перед вызовом каждой операции стек содержит нужное ей количество операндов, деление на ноль и переполнение не возникают, и, кроме того, в результате выполнения всех операций на стеке остаётся единственное число.
Формат результата работы программы. В стандартный поток вывода необходимо вывести число, оставшееся на вершине стека в результате выполнения последовательности операций.
circbuf.c
)Реализуйте операции InitQueue
, QueueEmpty
, Enqueue
и Dequeue
для
очереди целых чисел, представленной в виде кольцевого буфера. Начальный
размер буфера – $4$. В случае переполнения размер буфера должен увеличиваться
в два раза.
Составьте программу circbuf.c
, демонстрирующую работоспособность реализованных
операций.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит общее количество выполняемых операций $n$ $(0 < n \le 100\,000)$. Каждая из следующих $n$ строк содержит описание операции.
Операция либо имеет форму ENQ
$x$ (добавить число $x$ в хвост очереди,
$-2\,000\,000\,000 < x < 2\,000\,000\,000$), либо форму DEQ
(удалить головной
элемент из очереди), либо форму EMPTY
(проверить пустоту очереди).
Можно считать, что последовательность операций составлена правильно, то есть
перед каждой операцией DEQ
очередь не пуста.
Формат результата работы программы. Для каждой операции DEQ
вывести
в стандартный поток вывода значение удаляемого головного элемента очереди.
Для каждой операции EMPTY
вывести в стандартный поток вывода «true
» или
«false
» в зависимости от того, пуста очередь или нет.
qmax.c
)Реализуйте через двойной стек набор операций InitQueue
, Enqueue
, Dequeue
,
QueueEmpty
и Maximum
для работы с очередью целых чисел. Операция Maximum
возвращает максимальное целое число, в данный момент времени находящееся
в очереди. Операции Enqueue
, QueueEmpty
и Maximum
должны работать
за константное время, а операция Dequeue
— за амортизированное константное
время.
Составьте программу qmax.c
, демонстрирующую работоспособность реализованных
операций.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит общее количество выполняемых операций $n$ $(0 < n \le 100\,000)$. Каждая из следующих $n$ строк содержит описание операции.
Операция либо имеет форму ENQ
$x$ (добавить число $x$ в хвост очереди,
$-2\,000\,000\,000 < x < 2\,000\,000\,000$), либо форму DEQ
(удалить головной
элемент из очереди), либо форму MAX
(показать текущее максимальное число),
либо форму EMPTY
(проверить пустоту очереди).
Можно считать, что последовательность операций составлена правильно, то есть
перед каждой операцией DEQ
очередь не пуста.
Формат результата работы программы. Для каждой операции DEQ
вывести
в стандартный поток вывода значение удаляемого головного элемента очереди. Для
каждой операции MAX
вывести в стандартный поток вывода текущее максимальное
число. Для каждой операции EMPTY
вывести в стандартный поток вывода «true
»
или «false
» в зависимости от того, пуста очередь или нет.
Подсказка. Как реализовать стек с операциями Push
, Pop
и Maximum
за константное время?
merge.c
)Составьте программу merge.c
, объединяющую $k$ отсортированных по возрастанию
массивов целых чисел в один отсортированный массив за время $O(n \lg(k))$,
где $n$ — общее количество элементов во всех входных массивах. Для слияния
массивов воспользуйтесь очередью с приоритетами размера $k$.
Формат входных данных программы должен быть такой: первая строка, считываемая со стандартного потока ввода, содержит количество $k$ массивов, вторая строка содержит последовательность целых чисел $n_1, n_2, \ldots , n_k$, разделённых пробелами и задающих размеры массивов, каждая из следующих $k$ строк описывает соответствующий массив, то есть $i$-тая строка содержит $n_i$ целых чисел, отсортированных по возрастанию и разделённых пробелами.
Программа должна выводить в стандартный поток вывода отсортированную по возрастанию последовательность целых чисел, полученную путём слияния массивов. Целые числа должны разделяться пробелами или символами перевода строки.
Например, рассмотрим следующие входные данные:
4
3 5 1 2
10 12 20
15 16 17 19 25
20
11 12
Для этих данных программа должна вывести
10 11 12 12 15 16 17 19 20 20 25
cluster.c
)Имеется вычислительный кластер, состоящий из $N$ однопроцессорных узлов. На кластере нужно выполнить $M$ задач. Каждая задача описывается парой $\langle t_1,t_2 \rangle$, где $t_1$ — время в микросекундах от включения кластера, начиная с которого задачу можно посылать на выполнение (до этого времени входные данные для задачи неготовы); $t_2$ — прогнозируемое время выполнения задачи в микросекундах.
Для выполнения каждой задачи задействуется один узел кластера, то есть задачи невозможно распараллеливать. Кроме того, нельзя менять порядок выполнения задач: если данные для задачи $A$ оказываются подготовлены раньше, чем данные для задачи $B$, то задача $A$ не может быть запущена позже задачи $B$.
Необходимо составить программу cluster.c
, вычисляющую минимальное время
в микросекундах от начала работы кластера, когда все задачи будут выполнены.
В программе нужно использовать очередь с приоритетами для хранения времен
окончания задач, запущенных на кластере.
Формат входных данных программы должен быть такой: первая строка, считываемая со стандартного потока ввода, содержит количество $N$ узлов кластера, вторая строка содержит число $M$ задач, каждая из следующих $M$ строк содержит пару целых чисел $\langle t_1,t_2 \rangle$, описывающих задачу. Пары чисел отсортированы в порядке возрастания $t_1$, и, более того, в каждой паре $t_1$ уникально.
Программа должна вывести в стандартный поток вывода целое число, выражающее время в микросекундах, прошедшее от включения кластера до момента, когда все задачи будут выполнены.
listisort.c
)Составьте программу listisort.c
, выполняющую сортировку двунаправленного
кольцевого списка целых чисел по возрастанию. В программе должен быть
реализован алгоритм сортировки вставками.
Элементы списка должны быть представлены структурой
struct Elem {
struct Elem *prev, *next;
int v;
};
Алгоритм сортировки вставками, адаптированный для списков, должен выполнять не более $n$ обменов элементов, где $n$ — длина списка.
Программа должна считывать со стандартного потока ввода размер списка $n$ и значения элементов списка. В стандартный поток вывода должны выводиться элементы отсортированного списка.
listbsort.c
)Составьте программу listbsort.c
, выполняющую сортировку слов в предложении
в порядке возрастания их длин. Слова в предложении разделены одним или
несколькими пробелами. Программа должна формировать однонаправленный список
слов, а затем сортировать этот список пузырьком.
Функция bsort
, реализующая алгоритм сортировки, должна быть объявлена как
struct Elem *bsort(struct Elem *list)
{
...
}
Структура Elem
, указатели на которую фигурируют в объявлении функции bsort
,
должна представлять элемент однонаправленного списка, содержащего одно слово
из предложения:
struct Elem {
struct Elem *next;
char *word;
};
Исходное предложение подаётся в стандартный поток ввода программы. Программа должна вывести в стандартный поток вывода отсортированную последовательность слов, разделённых пробелами.
Максимальная длина исходной строки — $1000$ символов.
ranklist.c
)Операция Rank
: $A \times K \to \mathbb{N}$ для ассоциативного массива
вычисляет порядковый номер словарной пары с ключом $k$ в отсортированной
последовательности входящих в ассоциативный массив словарных пар. Пары
нумеруются, начиная с нуля.
Модифицируйте представление и реализацию списка с пропусками, чтобы операция
Rank
для него работала в среднем за время $O(\lg n)$.
Составьте программу ranklist.c
, демонстрирующую работоспособность
реализованной операции.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит общее количество выполняемых операций $n$ $(0 < n \le 100\,000)$. Каждая из следующих $n$ строк содержит описание операции.
Операция либо имеет форму INSERT
$k$ $v$ (добавить в список с пропусками
словарную пару, в которой ключ $k$ — целое число, значение $v$ — строка,
составленная из латинских букв; $-1\,000\,000\,000 < k < 1\,000\,000\,000$,
$len(v) < 10$), либо форму LOOKUP
$k$ (вывести строку, связанную с ключом
$k$), либо форму DELETE
$k$ (удалить строку, связанную с ключом $k$), либо
форму RANK
$k$ (вывести порядковый номер словарной пары с ключом $k$).
Можно считать, что последовательность операций составлена правильно.
Формат результата работы программы. Для каждой операции LOOKUP
вывести
в стандартный поток вывода строку, связанную с ключом $k$. Для каждой операции
RANK
вывести в стандартный поток вывода порядковый номер словарной пары
с ключом $k$.
Указание. Представление списка с пропусками нужно модифицировать следующим
образом: каждый элемент списка должен включать в себя массив целых чисел span
размера $m$, где $m$ — количество уровней в списке. При этом $i$-тый элемент
массива span
должен содержать расстояние от данного элемента до следующего
элемента на $i$-том уровне.
ranktree.c
)Операция SearchByRank
: $A \times \mathbb{N} \to P$ для ассоциативного
массива возвращает словарную пару с заданным номером в отсортированной
последовательности входящих в ассоциативный массив словарных пар.
Модифицируйте представление и реализацию бинарного дерева поиска, чтобы
операция SearchByRank
для него работала за время $O(h)$, где $h$ —
высота дерева.
Составьте программу ranktree.c
, демонстрирующую работоспособность
реализованной операции.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит общее количество выполняемых операций $n$ $(0 < n \le 100\,000)$. Каждая из следующих $n$ строк содержит описание операции.
Операция либо имеет форму INSERT
$k$ $v$ (добавить в дерево словарную пару,
в которой ключ $k$ — целое число, значение $v$ — строка, составленная
из латинских букв; $-1\,000\,000\,000 < k < 1\,000\,000\,000$, $len(v) < 10$),
либо форму LOOKUP
$k$ (вывести строку, связанную с ключом $k$), либо форму
DELETE
$k$ (удалить строку, связанную с ключом $k$), либо форму SEARCH
$x$ (вывести строку, связанную с ключом, имеющим порядковый номер $x$).
Можно считать, что последовательность операций составлена правильно.
Формат результата работы программы. Для каждой операции LOOKUP
вывести
в стандартный поток вывода строку, связанную с ключом $k$. Для каждой операции
SEARCH
вывести в стандартный поток вывода строку, связанную с ключом, имеющим
порядковый номер $x$.
Указание. Представление бинарного дерева поиска нужно модифицировать
следующим образом: в каждую вершину нужно добавить поле count
, содержащее
размер поддерева с корнем в данной вершине. Размер поддерева — это количество
вершин в нём.
lexavl.c
)Пусть константа — это непустая последовательность десятичных цифр.
Пусть специальный знак — это один из следующих символов: +
, -
, *
, /
,
(
, )
.
Пусть идентификатор — это непустая последовательность латинских букв и десятичных цифр, начинающаяся с буквы.
Пусть лексема — это либо константа, либо специальный знак, либо идентификатор.
Известно, что в некоторой строке записаны лексемы и пробелы. Лексемы не обязательно разделены пробелами за исключением случая, когда непосредственно после константы идёт идентификатор. Назовём такую строку предложением.
Лексический анализ предложения заключается в выделении из него
последовательности записанных в нём лексем. При этом для каждой лексемы
вычисляется пара $\langle tag,value \rangle$, где $tag$ — тип лексемы (CONST
для констант, SPEC
для специальных знаков и IDENT
для идентификаторов),
а $value$ — значение лексемы.
Значение лексемы — это неотрицательное целое число, смысл которого зависит от типа лексемы.
Константу мы будем считать десятичной записью её значения.
Значением специального знака пусть будет его порядковый номер в списке +
,
-
, *
, /
, (
, )
(нумерация осуществляется, начиная с нуля).
Значение идентификатора определяется следующим образом: если выписать все идентификаторы в том порядке, в каком они входят в предложение, и оставить только первые вхождения каждого идентификатора, то значением идентификатора будет являться его порядковый номер в получившейся последовательности (нумерация осуществляется, начиная с нуля).
Например, если дано предложение
alpha + x1 (beta alpha) x1 y
то значением идентификатора alpha
является число $0$, значением идентификатора
x1
— число $1$, значением beta
— число $2$, а значением y
— число $3$.
Составьте программу lexavl.c
, выполняющую лексический анализ предложения.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит размер предложения $n$. Следующая строка содержит само предложение.
Формат результата. Для каждой лексемы, выделенной из предложения, программа должна выводить в стандартный поток вывода её тип и значение.
Указание. В процессе лексического анализа необходимо использовать АВЛ-дерево, хранящее отображение идентификаторов в их значения.
Вместо выделения памяти под всю исходную строку и использования gets()
или
fgets()
, можно строку считывать посимвольно при помощи getchar()
, getc()
или fgetc()
.
disparray.c
)Разреженный массив — это массив большого размера, большинство элементов которого равны нулю. Хранение разреженного массива в памяти целиком нецелесообразно или даже вовсе невозможно из-за его большого размера, поэтому разумным решением является хранение только ненулевых элементов за счёт некоторого снижения скорости операций над массивом.
Разреженный целочисленный массив можно представить как ассоциативный массив, в котором и ключи, и значения являются целыми числами. При этом наличие в ассоциативном массиве словарной пары $\langle k,v \rangle$ означает, что $k$-й элемент разреженного массива равен $v$. Если же в ассоциативном массиве нет пары с ключом $k$, то считается, что $k$-тый элемент разреженного массива равен нулю.
Будем считать, что в ассоциативном массиве, представляющем разреженный массив, вообще нет словарных пар со значением ноль. Это означает, что если $k$-му элементу разреженного массива, содержащему ненулевое значение, присваивается ноль, то словарная пара с ключом $k$ вообще удаляется из ассоциативного массива.
Пусть $A$ — множество разреженных целочисленных массивов. Определим основные операции над разреженным целочисленным массивом:
At
: $A \times \mathbb{N} \to \mathbb{Z}$ — возвращает $k$-тый элемент
массива,Assign
: $A \times \mathbb{N} \times \mathbb{Z} \to A$ — присваивает новое
значение $k$-тому элементу массива.Пусть разреженный целочисленный массив представлен в виде хеш-таблицы
размера $m$. Реализуйте для него операции At
и Assign
. Составьте программу
disparray.c
, демонстрирующую работоспособность реализованных операций.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит общее количество выполняемых операций $n\,(0 < n \le 100\,000)$, а вторая — размер хеш-таблицы $m$. Каждая из следующих $n$ строк содержит описание операции.
Операция либо имеет форму ASSIGN
$i$ $v$ (присвоить значение $v$ элементу
разреженного массива с индексом $i$; $0 \le i < 1\,000\,000\,000$;
$-1\,000\,000\,000 < v < 1\,000\,000\,000)$, либо форму AT
$i$ (вывести
значение элемента разреженного массива с индексом $i$).
Формат результата работы программы. Для каждой операции AT
вывести
в стандартный поток вывода значение элемента разреженного массива
с индексом $i$.
Указание. Пусть хеш-функция вычисляется как $h(i)=i \mod m$.
zeroxor.c
)Пусть дана последовательность из $n$ целых чисел, где $0 < n \le 100\,000$,
и каждое целое число находится в диапазоне от $-2^{31}$ до $2^{31} - 1$.
Составьте программу zeroxor.c
, вычисляющую, сколько в последовательности
существует подпоследовательностей таких, что побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ их
элементов равно $0$.
Например, в последовательности $a = {0,14,14,2, 2,0}$ таких подпоследовательностей $10$ штук:
Программа должна считывать из стандартного потока ввода число $n$, а затем $n$ чисел, образующих последовательность. Программа должна выводить количество подпоследовательностей в стандартный поток вывода.
Указание. Пусть побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ подпоследовательностей $[0 : i]$ и $[0 : j]$ равно $x$, и не существует такого $k$, что $i < k < j$ и побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ подпоследовательности $[0 : k ]$ равно $x$.
Пусть также мы знаем количество подпоследовательностей с правой границей $i$, побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ которых равно $0$. Очевидно, что количество подпоследовательностей с правой границей $j$, побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ которых равно $0$, на единицу больше.
ptrie.c
)Реализуйте структуру данных, представляющую множество строк с операциями
Insert
(добавление строки в множество), Delete
(удаление строки
из множества) и Prefix
(подсчёт количества строк множества, имеющих указанных
префикс). Операции Insert
и Delete
должны работать за время $O(len(k))$,
где $k$ — добавляемая или удаляемая строка, а операция Prefix
— за время
$O(len(p))$, где $p$ — префикс.
Составьте программу ptrie.c
, демонстрирующую работоспособность реализованных
операций.
Формат входных данных. Первая строка, считываемая со стандартного потока ввода, содержит общее количество выполняемых операций $n$ $(0 < n \le 10000)$. Каждая из следующих $n$ строк содержит описание операции.
Операция либо имеет форму INSERT
$k$ (добавить в множество строку $k$, $0 <
len(k) < 100\,000$), либо форму DELETE
$k$ (удалить из множества имеющуюся
в нём строку $k$), либо форму PREFIX
$p$ (вычислить количество строк
в множестве, имеющих префикс $p$).
Отметим, что аргументы операций — это строки, составленные из маленьких латинских букв.
Кроме того, допустим вызов операции INSERT
для строки, уже присутствующей
в множестве.
Формат результата работы программы. Для каждой операции PREFIX
вывести
в стандартный поток вывода количество строк в множестве, имеющих указанный
префикс.