#include #include #include #include #include #include using namespace std; // Templatex from KACTL using vi = vector; #define rep(i, a, b) for(int i = a; i < (b); ++i) #define all(x) begin(x), end(x) #define sz(x) (int)(x).size() struct RMQ { using T = int; T f(T a, T b) { return min(a, b); } vector> jmp; RMQ(const vector& V) : jmp(1, V) { for (size_t pw = 1, k = 1; pw * 2 <= V.size(); pw *= 2, ++k) { jmp.emplace_back(V.size() - pw * 2 + 1); for (int j = 0; j < jmp[k].size(); ++j) { jmp[k][j] = f(jmp[k - 1][j], jmp[k - 1][j + pw]); } } } // [a, b) T query(int a, int b) { assert(a < b); // or return inf if a == b int dep = 31 - __builtin_clz(b - a); return f(jmp[dep][a], jmp[dep][b - (1 << dep)]); } }; struct SuffixArray { vi sa, lcp; SuffixArray(string& s, int lim=256) { // or basic_string int n = sz(s) + 1, k = 0, a, b; vi x(all(s)+1), y(n), ws(max(n, lim)), rank(n); sa = lcp = y, iota(all(sa), 0); for (int j = 0, p = 0; p < n; j = max(1, j * 2), lim = p) { p = j, iota(all(y), n - j); rep(i,0,n) if (sa[i] >= j) y[p++] = sa[i] - j; fill(all(ws), 0); rep(i,0,n) ws[x[i]]++; rep(i,1,lim) ws[i] += ws[i - 1]; for (int i = n; i--;) sa[--ws[x[y[i]]]] = y[i]; swap(x, y), p = 1, x[sa[0]] = 0; rep(i,1,n) a = sa[i - 1], b = sa[i], x[b] = (y[a] == y[b] && y[a + j] == y[b + j]) ? p - 1 : p++; } rep(i,1,n) rank[sa[i]] = i; for (int i = 0, j; i < n - 1; lcp[rank[i++]] = k) for (k && k--, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k++); } }; /** * Let dp[i] be the min cost to write out the first i characters of the string, using backreferences * * One way to do that is to just add the last character, so * dp[i] <- dp[i - 1] + 1 * * Another way is to use a backreference. The goal is to find the largest * backreference possible. As a formula, for each index i, we want to find the * maximum k such that there exists an index j such that LCP(i - k, j) >= k. * * Where LCP(j, i) is the longest common prefix of the substrings beginning at * i and j. * * To find this k, we invert the problem a bit. FOr a fixed LCP(j, i) value, * which indices could use this LCP? The answer is the indices between i and i + LCP(j, i), inclusive. Therefore we only care about the maximum LCP(j, i) for * a fixed i. To find this maximum LCP, we want to find the nearest values in * the suffix array since the LCPs are monotonically decreasing away from i. * Thus for each index i, we find the values in the suffix array before and * after it in the suffix array that are < i. This can be done using a stack in * both directions. * * Overall runtime: O(n log n). */ int main() { string s; getline(cin, s); int n = s.size(); SuffixArray sa(s); RMQ rmq(sa.lcp); // pref_lcp[i] is the maximum of all lcp(j, i) where j < i // To compute this, find the j < i immediately before and after i in the suffix // array. This can be done using a stack. vector pref_lcp(n + 1, 0); { vector stk; for (int i = 0; i <= n; ++i) { while (!stk.empty() && sa.sa[stk.back()] > sa.sa[i]) { stk.pop_back(); } if (!stk.empty()) { int j = stk.back(); assert(sa.sa[j] < sa.sa[i]); int k = rmq.query(j + 1, i + 1); pref_lcp[sa.sa[i]] = max(pref_lcp[sa.sa[i]], k); } stk.push_back(i); } } { vector stk; for (int i = n; i >= 0; --i) { while (!stk.empty() && sa.sa[stk.back()] > sa.sa[i]) { stk.pop_back(); } if (!stk.empty()) { int j = stk.back(); assert(sa.sa[j] < sa.sa[i]); int k = rmq.query(i + 1, j + 1); pref_lcp[sa.sa[i]] = max(pref_lcp[sa.sa[i]], k); } stk.push_back(i); } } priority_queue, vector>, greater>> pq; pq.emplace(0, pref_lcp[0]); vector dp(n + 1, n); dp[0] = 0; for (int i = 1; i <= n; ++i) { dp[i] = dp[i - 1] + 1; while (!pq.empty() && pq.top().second < i) { pq.pop(); } if (!pq.empty()) { dp[i] = min(dp[i], dp[pq.top().first] + 3); } pq.emplace(i, i + pref_lcp[i]); } cout << dp[n] << '\n'; return 0; }