Реши задачу на c++ Адрес является строкой s, состо...
創建於:2026年1月10日
使用 GPT-5.2 Thinking 回答 Chat01
創建於:2026年1月10日
使用 GPT-5.2 Thinking 回答 Chat01
Реши задачу на c++ Адрес является строкой s, состоящей из n строчных букв латинского алфавита. В форму для
заказа такси нельзя вводить адреса длиннее, чем q букв.
Сжатым видом строки s будем называть строку t = [a1][f1][a2][f2] . . . [am][fm], где ai — некоторые
целые положительные числа, а fi — некоторые строки. Если выписать строку f1 a1 раз, затем —
строку f2 a2 раз, и т. д., должна получиться строка s — исходный адрес.
Сжатую строку может и не получиться ввести в форму, тогда можно немного изменить адрес,
чтобы длина сжатого вида строки была меньше или равна q. Нет сомнений, что водитель легко
сможет понять и измененный адрес. Но полученный водителем адрес должен отличаться от оригинального в как можно меньшем количестве мест и иметь такую же длину. Проще говоря, необходимо
найти такой сжатый вид строки t2, что ее полный вид равен s2, |s2| = |s| = n, и количество таких
позиций i ∈ {1, . . . , n}, что s2i ̸= si
, как можно меньше.
Какое минимальное количество отличий от оригинала может быть в полученном водителем адресе?
Формат входных данных
В первой строке находятся два целых числа n и q (3 ⩽ n ⩽ 500, 2 ⩽ q ⩽ n − 1) — длина
изначальной строки и максимальная допустимая длина сжатой строки.
Во второй строке находится строка s длины n, состоящая только из строчных букв латинского
алфавита — адрес.
Формат выходных данных
Если изменением символов возможно получить сжатый вид строки длины, не превосходящей k,
выведите единственное целое число — минимальное количество отличий сжатого адреса от оригинального.
В противном случае, выведите число −1.
Сжатая запись имеет вид
a1 f1 a2 f2 ... am fm,
где ai записывается десятичными цифрами (без лидирующих нулей), fi — строка из букв, и при раскрытии получается исходная строка: f1 повторили a1 раз, потом f2 — a2 раз и т.д.
Мы можем заменить некоторые символы исходного адреса s, получив строку s2 той же длины n, чтобы существовала такая запись длиной <= q, а число замен было минимальным.
(a, f)Если блок покрывает отрезок длины L, а |f| = p, то:
L = a * pp.Минимальное число замен, чтобы сделать отрезок периодическим с периодом p:
0..p-1 (по mod p) выбираем букву, которая встречается там чаще всего;Пусть dp[i][len] = минимальные замены на префиксе длины i, если длина сжатой записи ровно len.
Переход:
[i .. j-1] длины L = j-ip | L, тогда a = L/pdigits(a) + ppОтвет: min_{len<=q} dp[n][len], если достижимо, иначе -1.
n <= 500, поэтому:
l, периодов p и числа повторов a (то есть длин a*p)cpp#include <bits/stdc++.h> using namespace std; struct Edge { uint16_t to; // позиция конца (длина префикса) uint16_t w; // добавка к длине сжатой строки uint16_t cost; // сколько замен на этом сегменте }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, q; cin >> n >> q; string s; cin >> s; // digits[x] = число десятичных цифр в x (x <= 500 => максимум 3) vector<int> digits(n + 1, 0); for (int x = 1; x <= n; x++) { if (x < 10) digits[x] = 1; else if (x < 100) digits[x] = 2; else digits[x] = 3; } // divisors[L] = все p, такие что p делит L vector<vector<int>> divisors(n + 1); for (int p = 1; p <= n; p++) { for (int L = p; L <= n; L += p) divisors[L].push_back(p); } // --- Предвычисление cost(start=l, period=p, repeats=a) --- // Храним в одном пуле: pool[ offset[l,p] + a ] = стоимость для длины a*p. // offset размером n*(n+1), p от 1..n vector<int> offset(n * (n + 1), -1); // посчитаем, сколько всего значений будет в пуле, чтобы reserve long long total = 1; // dummy 0 for (int l = 0; l < n; l++) { int rem = n - l; for (int p = 1; p <= rem; p++) total += rem / p; } vector<uint16_t> pool; pool.reserve((size_t)total); pool.push_back(0); // dummy // рабочие массивы (макс p = 500) static uint16_t cnt[500 * 26]; static uint16_t best[500]; for (int l = 0; l < n; l++) { int rem = n - l; for (int p = 1; p <= rem; p++) { int maxA = rem / p; // максимум повторов int idx = l * (n + 1) + p; offset[idx] = (int)pool.size(); // начало блока pool.resize(pool.size() + (size_t)maxA + 1); // обнуляем только нужную часть memset(cnt, 0, (size_t)p * 26 * sizeof(uint16_t)); memset(best, 0, (size_t)p * sizeof(uint16_t)); int sumBest = 0; // сумма максимумов по классам (0..p-1) for (int a = 1; a <= maxA; a++) { int base = l + (a - 1) * p; for (int r = 0; r < p; r++) { int ch = s[base + r] - 'a'; uint16_t &cell = cnt[r * 26 + ch]; cell++; if (cell > best[r]) { best[r] = cell; sumBest++; // максимум в этом классе вырос на 1 } } int L = a * p; int mism = L - sumBest; pool[(size_t)offset[idx] + a] = (uint16_t)mism; } } } // --- Построим ребра (сразу отфильтруем по w <= q) --- vector<vector<Edge>> edges(n); for (int i = 0; i < n; i++) { int maxL = n - i; edges[i].reserve(2000); for (int L = 1; L <= maxL; L++) { int to = i + L; for (int p : divisors[L]) { int a = L / p; int w = digits[a] + p; if (w > q) continue; int idx = i * (n + 1) + p; uint16_t c = pool[(size_t)offset[idx] + a]; edges[i].push_back(Edge{(uint16_t)to, (uint16_t)w, c}); } } // необязательно, но можно чуть сжать: оставить минимальную cost для одинаковых (to,w) sort(edges[i].begin(), edges[i].end(), [](const Edge& A, const Edge& B){ if (A.to != B.to) return A.to < B.to; if (A.w != B.w) return A.w < B.w; return A.cost < B.cost; }); vector<Edge> compact; compact.reserve(edges[i].size()); for (auto &e : edges[i]) { if (!compact.empty() && compact.back().to == e.to && compact.back().w == e.w) { compact.back().cost = min(compact.back().cost, e.cost); } else compact.push_back(e); } edges[i].swap(compact); } // --- DP --- const uint16_t INF = 10000; int W = q + 1; vector<uint16_t> dp((n + 1) * W, INF); dp[0 * W + 0] = 0; vector<int> active; active.reserve(W); for (int i = 0; i < n; i++) { size_t baseI = (size_t)i * W; active.clear(); for (int len = 0; len <= q; len++) { if (dp[baseI + len] != INF) active.push_back(len); } if (active.empty()) continue; for (const auto &e : edges[i]) { int w = e.w; int limit = q - w; size_t baseTo = (size_t)e.to * W; for (int len : active) { if (len > limit) break; uint16_t cur = dp[baseI + len]; uint16_t nv = (uint16_t)(cur + e.cost); uint16_t &ref = dp[baseTo + len + w]; if (nv < ref) ref = nv; } } } uint16_t ans = INF; for (int len = 0; len <= q; len++) ans = min(ans, dp[(size_t)n * W + len]); if (ans == INF) cout << -1 << "\n"; else cout << ans << "\n"; return 0; }
Если хочешь — могу добавить вывод самого оптимального изменённого адреса s2 и/или саму сжатую запись (с восстановлением решения), но по условию требуется только минимальное число отличий.