import java.io.OutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.stream.IntStream; import java.util.Arrays; import java.io.BufferedWriter; import java.util.InputMismatchException; import java.io.Writer; import java.io.OutputStreamWriter; /** * Built using CHelper plug-in * Actual solution is at the top * * @author lewin */ public class cram_lewin_nlogn { public static void main(String[] args) { InputStream inputStream = System.in; OutputStream outputStream = System.out; InputReader in = new InputReader(inputStream); OutputWriter out = new OutputWriter(outputStream); cram solver = new cram(); solver.solve(1, in, out); out.close(); } static class cram { public void solve(int testNumber, InputReader in, OutputWriter out) { String s = in.nextLine(); int n = s.length(); int[] sa = SuffixArray.suffixArray(s); int[] isa = AUtils.inverse(sa); // prev/next contain pointers to prev/next element in suffix array with smaller index // do this efficiently with a doubly linked list and remove nodes from big to small index int[] prev = new int[n], next = new int[n]; Arrays.fill(prev, -1); Arrays.fill(next, -1); for (int i = 1; i < n; i++) prev[sa[i]] = sa[i - 1]; for (int i = 0; i + 1 < n; i++) next[sa[i]] = sa[i + 1]; for (int i = n - 1; i >= 0; i--) { if (next[i] != -1) prev[next[i]] = prev[i]; if (prev[i] != -1) next[prev[i]] = next[i]; } int[] lcp = SuffixArray.lcp(sa, s); RmqSparseTable rmq = new RmqSparseTable(lcp); int[] dp = new int[n + 1]; Arrays.fill(dp, 1 << 30); dp[0] = 0; for (int i = 0; i < n; i++) { int pos = isa[i]; int ext = 0; if (prev[i] != -1) ext = Math.max(ext, rmq.min(isa[prev[i]], pos - 1)); if (next[i] != -1) ext = Math.max(ext, rmq.min(pos, isa[next[i]] - 1)); dp[i + 1] = Math.min(dp[i + 1], dp[i] + 1); dp[i + ext] = Math.min(dp[i + ext], dp[i] + 3); } out.println(dp[n]); } } static class RmqSparseTable { int[] logTable; int[][] rmq; int[] a; public RmqSparseTable(int[] a) { this.a = a; int n = a.length; logTable = new int[n + 1]; for (int i = 2; i <= n; i++) logTable[i] = logTable[i >> 1] + 1; rmq = new int[logTable[n] + 1][n]; for (int i = 0; i < n; ++i) rmq[0][i] = i; for (int k = 1; (1 << k) < n; ++k) { for (int i = 0; i + (1 << k) <= n; i++) { int x = rmq[k - 1][i]; int y = rmq[k - 1][i + (1 << k - 1)]; rmq[k][i] = a[x] <= a[y] ? x : y; } } } public int min(int i, int j) { if (i > j) return 1 << 30; int k = logTable[j - i]; int x = rmq[k][i]; int y = rmq[k][j - (1 << k) + 1]; return Math.min(a[x], a[y]); } } static class InputReader { private InputStream stream; private byte[] buf = new byte[1 << 16]; private int curChar; private int numChars; public InputReader(InputStream stream) { this.stream = stream; } public int read() { if (this.numChars == -1) { throw new UnknownError(); } else { if (this.curChar >= this.numChars) { this.curChar = 0; try { this.numChars = this.stream.read(this.buf); } catch (IOException var2) { throw new InputMismatchException(); } if (this.numChars <= 0) { return -1; } } return this.buf[this.curChar++]; } } public String nextLine() { int c; while (isSpaceChar(c = this.read())) { ; } StringBuilder result = new StringBuilder(); result.appendCodePoint(c); while (!isEndOfLine(c = this.read())) { if (c != '\r') result.appendCodePoint(c); } return result.toString().trim(); } public static boolean isEndOfLine(int c) { return c == '\n' || c == -1; } public static boolean isSpaceChar(int c) { return c == 32 || c == 10 || c == 13 || c == 9 || c == -1; } } static class OutputWriter { private final PrintWriter writer; public OutputWriter(OutputStream outputStream) { writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream))); } public OutputWriter(Writer writer) { this.writer = new PrintWriter(writer); } public void close() { writer.close(); } public void println(int i) { writer.println(i); } } static class AUtils { public static int[] inverse(int[] arr) { int n = arr.length; int[] ret = new int[n]; for (int i = 0; i < n; i++) { ret[arr[i]] = i; } return ret; } } static class SuffixArray { public static int[] suffixArray(CharSequence S) { int n = S.length(); // stable sort of characters int[] sa = IntStream.range(0, n).mapToObj(i -> n - 1 - i). sorted((a, b) -> Character.compare(S.charAt(a), S.charAt(b))).mapToInt(Integer::intValue).toArray(); int[] classes = S.chars().toArray(); // sa[i] - suffix on i'th position after sorting by first len characters // classes[i] - equivalence class of the i'th suffix after sorting by first len characters for (int len = 1; len < n; len *= 2) { int[] c = classes.clone(); for (int i = 0; i < n; i++) { // condition sa[i - 1] + len < n simulates 0-symbol at the end of the string // a separate class is created for each suffix followed by simulated 0-symbol classes[sa[i]] = i > 0 && c[sa[i - 1]] == c[sa[i]] && sa[i - 1] + len < n && c[sa[i - 1] + len / 2] == c[sa[i] + len / 2] ? classes[sa[i - 1]] : i; } // Suffixes are already sorted by first len characters // Now sort suffixes by first len * 2 characters int[] cnt = IntStream.range(0, n).toArray(); int[] s = sa.clone(); for (int i = 0; i < n; i++) { // s[i] - order of suffixes sorted by first len characters // (s[i] - len) - order of suffixes sorted only by second len characters int s1 = s[i] - len; // sort only suffixes of length > len, others are already sorted if (s1 >= 0) sa[cnt[classes[s1]]++] = s1; } } return sa; } public static int[] lcp(int[] sa, CharSequence s) { int n = sa.length; int[] rank = new int[n]; for (int i = 0; i < n; i++) rank[sa[i]] = i; int[] lcp = new int[n - 1]; for (int i = 0, h = 0; i < n; i++) { if (rank[i] < n - 1) { for (int j = sa[rank[i] + 1]; Math.max(i, j) + h < s.length() && s.charAt(i + h) == s.charAt(j + h); ++h) ; lcp[rank[i]] = h; if (h > 0) --h; } } return lcp; } } }