• [APIO2014]回文串


    本博客是写给那些没学过SAM(因为博主自己也没学过),想用Manacher+后缀数组做这道题的人的

    题目大意

    定义一个子串的价值为它在原串中出现的次数与它的长度的乘积,求最大回文子串的价值

    思路

    首先我们上一个马拉车,求出所有本质不同的回文子串。你也许会问,它们的数量难道不会爆int吗?事实上,它们最多只有(lvert S vert)个(S为原串),证明如下:
    考虑数学归纳法,令(n)为原串的长度
    1.(n=1)时,显然成立
    2.若(n=k)时成立,来证明(n=k+1)时成立。考虑在长度为(n)的串后加上一个字符,则本质不同的回文串最多增加一个。反证,假设有多个,则一定有一个最长的,而且它们都包含新增的那个字符。故所有的比它短的新增回文子串都可以沿它的对称轴对称过去,得到一个和它一样的回文子串,且对称过去得到的子串一定也是原来长度为(n)的串的子串,与本质不同矛盾。故最多增加一个,所以(n=k+1)时也成立
    3.故对于所有正整数(n),结论都成立
    那怎么用Manacher来(O(n))地求出所有本质不同的回文子串呢?在算法中我们记录了一个表示所有回文子串最远能扩展到的位置(pos)。若当前位置的回文子串包含于(pos)中,表明一个跟他一毛一样的串在之前已经出现过了,否则,也就是(i+f[i]>pos)时,表明出现了一个本质不同的回文子串,我们统计一下它的贡献就好了。
    要求一个子串的出现次数,我们可以在(height)数组上ST表+二分,然后就没啦!

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define N 500000
    
    struct S {
        int x, a[2];
    }v[N+5], tmp[N+5];
    
    char s[4*N+5];
    int n, f[4*N+5], sa[N+5], rk[N+5], height[N+5], sum[N+5], st[N+5][30];
    long long ans = 0;
    
    int radixSort(int sz) { //sz:字符集大小
        for(int j = 0; j <= 1; ++j) {
            memset(sum, 0, sizeof sum);
            for(int i = 1; i <= n; ++i) sum[v[i].a[j]]++;
            for(int i = 1; i <= sz; ++i) sum[i] += sum[i-1];
            for(int i = n; i >= 1; --i) tmp[sum[v[i].a[j]]--] = v[i];
            for(int i = 1; i <= n; ++i) v[i] = tmp[i];
        }
        int cnt = 0;
        for(int i = 1; i <= n; ++i)
            if(v[i].a[0]==v[i-1].a[0] && v[i].a[1]==v[i-1].a[1]) rk[v[i].x] = cnt;
            else rk[v[i].x] = ++cnt;
        return cnt;
    }
    
    void buildSA() {
        for(int i = 1; i <= n; ++i) v[i].x = i, v[i].a[0] = 0, v[i].a[1] = s[i];
        int cnt = radixSort(255);
        for(int l = 1; cnt < n; l <<= 1) {
            for(int i = 1; i <= n; ++i) v[i].x = i, v[i].a[0] = i+l>n?0:rk[i+l], v[i].a[1] = rk[i];
            cnt = radixSort(cnt);
        }
        for(int i = 1; i <= n; ++i) sa[rk[i]] = i;
        for(int i = 1, b = 0, c; i <= n; height[rk[i++]] = b) //注意height和原数组是错位的
            for(b ? --b : 0, c = sa[rk[i]-1]; s[i+b] == s[c+b]; ++b);
    }
    
    int Log2[N+5];
    void buildST() { //构建ST表 
        for(int i = 1; i <= n; ++i) st[i][0] = height[i];
        for(int i = 2; i <= n; ++i) Log2[i] = Log2[i>>1]+1;
        for(int j = 1; (1<<j) <= n; ++j)
            for(int i = 1; i+(1<<j)-1 <= n; ++i)
                st[i][j] = min(st[i][j-1], st[i+(1<<j-1)][j-1]);
    }
    
    int query(int L, int R) { //LCP
        int x = Log2[R-L+1];
        return min(st[L][x], st[R-(1<<x)+1][x]);
    }
    
    void calc(int l, int r) {
        int x = rk[l+1>>1], len = (r>>1)-(l+1>>1)+1; //回到原串中的位置 
        int cnt = 1;
        int L = 1, R = x, mid, t = R;
        while(L <= R) { //向前二分 
            mid = L+R>>1;
            if(query(mid, x) >= len) R = mid-1, t = mid;
            else L = mid+1;
        }
        if(height[t] >= len) cnt += x-t+1;
        L = x+1, R = n, t = L;
        while(L <= R) { //向后二分 
            mid = L+R>>1;
            if(query(x+1, mid) >= len) L = mid+1, t = mid;
            else R = mid-1;
        }
        if(height[t] >= len) cnt += t-x;
        ans = max(ans, 1LL*len*cnt);
    }
    
    void Manacher() {
        s[2*n+1] = '#';
        for(int i = n; i >= 1; --i) s[2*i] = s[i], s[2*i-1] = '#'; //不要忘了统计长度为偶数的回文子串
        int mx = 0, pos = 0;
        for(int i = 1; i < 2*n+1; ++i) {
            if(mx > i) f[i] = min(f[2*pos-i], mx-i);
            else f[i] = 1;
            while(i-f[i] && s[i-f[i]] == s[i+f[i]]) {
                f[i]++;
                if(i+f[i] > mx) calc(i-f[i]+1, i+f[i]-1); //发现了一个本质不同的回文子串 
            }
            if(i+f[i] > mx) mx = i+f[i], pos = i; //更新边界 
        }
    }
    
    int main() {
        scanf("%s", s+1);
        n = strlen(s+1);
        buildSA();
        buildST();
        Manacher();
        printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    C++ Primer中的一个sort算法源码
    字符串反转操作,网易的一道面试题
    字符编码之UCS2与Utf8
    我的Vim配置
    使用PreviousPage来获取前一页页面的元素
    ToString()用法大全
    MSSQL怎样使自动增加的id列数据归零
    确定要离开当前页面吗
    js 获取url参数
    C#判断程序是否以管理员身份运行,否则以管理员身份重新打开 转载
  • 原文地址:https://www.cnblogs.com/dummyummy/p/10312543.html
Copyright © 2020-2023  润新知