Submission #1210580

#TimeUsernameProblemLanguageResultExecution timeMemory
1210580uranium235Palinilap (COI16_palinilap)Java
100 / 100
211 ms38884 KiB
//package ojuz;

import java.io.*;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

public class palinilap {
    public static final long m = (1L << 61) - 1, b = ThreadLocalRandom.current().nextLong(m - 1);
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        String s = reader.readLine();
        int n = s.length();

        Hash hash = new Hash(s);
        int l = 0, r = 0;
        long base = 0;
        long[][] buffs = new long[n][26];
        long[] nerfs = new long[n + 2];
        while (r < n) {
            int bound = Math.min(l + 1, n - r);
            int best = hash.palindrome(l, r);
            base += best;
            // the letters that break the palindrome
            int left = l - best;
            int right = r + best;
            // if palindrome can be extended
            if (best < bound) {
                // swap the positions if length is even and letters dont match
                int add = 1 + ((left > 0 && right < n - 1) ? hash.palindrome(left - 1, right + 1) : 0);
                // change the right side to the left, or vice versa leads to more palindromes
                buffs[left][s.charAt(right) - 'a'] += add;
                buffs[right][s.charAt(left) - 'a'] += add;
            }
            int leftBound = l + (l == r ? 0 : 1);
            int rightBound = r - (l == r ? 0 : 1);
            if (left + 1 < leftBound) {
                // polynomial update from left + 1(inclusive) to leftBound(exclusive)
                // difference array is thus [...1, 1, 1, 1, 1, -n, ...]
                // difference array of that is thus [...1, 0, 0, 0, 0, -n - 1, n, ...]
                nerfs[left + 1]++;
                nerfs[leftBound] -= (leftBound - left - 1);
                nerfs[leftBound]--;
                nerfs[leftBound + 1] += (leftBound - left - 1);
            }
            if (right - 1 > rightBound) {
                // polynomial update from rightBound(exclusive) to right - 1(inclusive)
                // difference array is thus [...n, -1, -1, -1, -1, -1, ...]
                // difference array of that is thus [...n, -1 - n, 0, 0, 0, 0, 1, ...]
                nerfs[rightBound + 1] += (right - 1 - rightBound);
                nerfs[rightBound + 2] -= (right - 1 - rightBound);
                nerfs[rightBound + 2]--;
                nerfs[right + 1]++;
            }

            if (l < r) l++;
            else r++;
        }
        for (int i = 1; i <= n; i++) nerfs[i] += nerfs[i - 1];
        for (int i = 1; i <= n; i++) nerfs[i] += nerfs[i - 1];

        long ans = 0;
        for (int i = 0; i < n; i++) {
            long max = 0;
            for (long j : buffs[i]) max = Math.max(max, j);
            ans = Math.max(ans, max - nerfs[i]);
        }

        System.out.println(base + ans);
    }

    public static long mult(long a, long b) {
        long low = a * b;
        return ((low & m) + ((Math.multiplyHigh(a, b) << 3) | (low >>> 61))) % m;
    }

    static class Hash {
        public final String s;
        public final long[] pows, hash, rash;
        public Hash(String s) {
            this.s = s;
            this.pows = new long[s.length() + 1];
            pows[0] = 1;
            for (int i = 1; i < pows.length; i++) pows[i] = mult(b, pows[i - 1]);
            this.hash = new long[s.length() + 1];
            for (int i = 1; i < hash.length; i++) hash[i] = (mult(b, hash[i - 1]) + s.charAt(i - 1)) % m;
            this.rash = new long[s.length() + 1];
            for (int i = rash.length - 2; i >= 0; i--) rash[i] = (mult(b, rash[i + 1]) + s.charAt(i)) % m;
        }

        public long hash(int a, int b) {
            return (hash[b + 1] - mult(hash[a], pows[b - a + 1]) + m) % m;
        }

        public long rash(int a, int b) {
            return (rash[a] - mult(rash[b + 1], pows[b - a + 1]) + m) % m;
        }

        public int palindrome(int l, int r) {
            if (s.charAt(l) != s.charAt(r)) return 0;
            // longest "palindrome" from l going left and from r going right
            int hi = Math.min(l + 1, s.length() - r);
            int lo = 0;
            while (lo < hi) {
                int mid = lo + (hi - lo + 1) / 2;
                if (hash(r, r + mid - 1) == rash(l - mid + 1, l)) lo = mid;
                else hi = mid - 1;
            }
            return lo;
        }
    }
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...