Longest Palindromic Substring in Linear Time with Hashing

Revision en1, by dajeff, 2025-02-25 23:01:50
#include <bits/stdc++.h>
 
using namespace std;
 
const int P = 31;
const long long MOD = 1079069296912181;  // https://bigprimes.org/
const int MAXN = 1e6 + 10;
 
long long pows[MAXN];
long long inv[MAXN];
 
vector<int> solve(string &s, bool odd) {
    long long lhash = 0;
    long long rhash = lhash;
    int res = 1, resl = 0;
    for (int l = 0, r = odd ? 0 : 1; l < static_cast<int>(s.length()) && r < static_cast<int>(s.length());) {
        if (0 <= l && r < s.length() && lhash == rhash && s[l] == s[r]) {
            int potential = r - l + 1;
            if (potential > res) {
                res = potential;
                resl = l;
            }
 
            lhash *= P;
            lhash += s[l--] - 'a' + 1;
            lhash %= MOD;
 
            rhash *= P;
            rhash += s[r++] - 'a' + 1;
            rhash %= MOD;
        } else {
            if (r == static_cast<int>(s.length()) - 1) {
                break;
            }
 
            int cl = (l + r) >> 1, cr = (l + r + 1) >> 1;
            lhash -= s[l + 1] - 'a' + 1;
            lhash += static_cast<long long>(static_cast<__int128>(pows[cl - l]) * (s[cl + 1] - 'a' + 1) % MOD);
            lhash %= MOD;
            lhash = static_cast<long long>(static_cast<__int128>(lhash) * inv[1] % MOD);
            lhash %= MOD;
            lhash += MOD;
            lhash %= MOD;
            ++l;
 
            rhash = static_cast<long long>(static_cast<__int128>(rhash) * P % MOD);
            rhash -= static_cast<long long>(static_cast<__int128>(pows[r - cr]) * (s[cr] - 'a' + 1) % MOD);
            rhash %= MOD;
            rhash += MOD;
            rhash %= MOD;
            rhash += s[r] - 'a' + 1;
            rhash %= MOD;
            ++r;
        }
    }
 
    return {resl, res};
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
 
    string s;
    cin >> s;
 
    pows[0] = 1;
    inv[0] = 1;
    for (int i = 1; i < MAXN; ++i) {
        pows[i] = pows[i - 1] * P % MOD;
        inv[i] = static_cast<long long>(static_cast<__int128>(inv[i - 1]) * 800599800934844 % MOD);
    }
 
    vector<int> odd = solve(s, false), even = solve(s, true);
    if (odd[1] >= even[1]) {
        cout << s.substr(odd[0], odd[1]) << endl;
    } else {
        cout << s.substr(even[0], even[1]) << endl;
    }
 
    return 0;
}
Tags hashing, palindrome

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en8 English dajeff 2025-03-12 10:43:41 0 (published)
en7 English dajeff 2025-03-12 10:43:07 224 Tiny change: 'cases. If adding `s' -> 'cases. If extending our candidate window by adding `s'
en6 English dajeff 2025-03-12 10:37:05 1069
en5 English dajeff 2025-03-12 09:52:37 553 Tiny change: 'bbf58f/)\n```cpp\n' -> 'bbf58f/)\n\n```cpp\n'
en4 English dajeff 2025-03-12 09:46:17 0
en3 English dajeff 2025-03-12 09:46:17 1508
en2 English dajeff 2025-03-12 09:44:10 2372
en1 English dajeff 2025-02-25 23:01:50 2480 Initial revision (saved to drafts)