manacher(马拉车)算法

时间复杂度 图片说明
求解一个字符串的最长回文子串长度的问题。
注:先插入 n+1 个 # ,再设置一个 po,维护已知区域 (po-Next[po],po+Next[po]),即维护 po。

char s[maxn], t[maxn<<1];  // s 为文本串 t 为加 # 串
int Next[maxn<<1]; // t 中以 i 为中心的最长回文半径

int manacher(char s[]) {
    int n = strlen(s+1);
    for(int i = 1; i <= n+1; ++ i) {
        t[(i<<1)-1] = '#';
        t[(i<<1)] = s[i];
    }
    int po = 1, ans = 0;
    Next[po] = 1;
    for(int i = 2; i <= (n<<1|1); ++ i) {
        if (Next[(po<<1)-i] + i < Next[po] + po) Next[i] = Next[(po<<1)-i];
        else { // 已知区间全部匹配
            int j = (Next[po] + po <= i) ? 1 : Next[po] + po - i; // i 不在已知区域 j 为 1
            while(t[i+j] && t[i-j] && t[i+j] == t[i-j]) ++j;
            Next[po=i] = j;
        }
        ans = max(Next[i]-1, ans);
    }
    return ans;
}

带权回文串(exkmp)

char s[maxn], t[maxn<<1];  // s 为文本串 t 为加 # 串
int Next[maxn<<1]; // t 中以 i 为中心的最长回文半径
int a[27];      // 不同字母有不同点值
int p[maxn<<1]; // 回文串的权值
int pre[maxn];  // 前缀回文串权值
int suf[maxn];  // 后缀回文串权值

void manacher(char s[]) {
    int n = strlen(s+1);
    for(int i = 1; i <= n+1; ++ i) {
        if (i <= n) pre[i] = suf[i] = 0; // 初始化
        t[(i<<1)-1] = 'a'-1;
        t[(i<<1)] = s[i];
    }
    int po, pw = 0, l = 0, r = n+1<<1;
    Next[po=l+1] = 1;
    for(int i = l+2; i < r-1; ++ i) {  // 第一个和最后一个 # 不需要判断
        pw +=  a[t[i]-'a'+1] * 2;
        if (Next[(po<<1)-i] + i < Next[po] + po) Next[i] = Next[(po<<1)-i], p[i] = p[(po<<1)-i];
        else { // 已知区间全部匹配
            int j = (po + Next[po] <= i) ? 1 : Next[po] + po - i;
            p[i] = (po + Next[po] <= i ? 0 : p[po] - pw) +  (pw = a[t[i]-'a'+1]); // 回文串的权值
            while(i-j>l && i+j<r && t[i+j] == t[i-j]) p[i] += 2*a[t[i+j]-'a'+1], ++j;
            Next[po=i] = j;
        }
        if (i - Next[i] == l) pre[  Next[i]-1] = p[i]; // 前缀回文串权值
        if (i + Next[i] == r) suf[n-Next[i]+2] = p[i]; // 后缀回文串权值
    }
}