• bzoj3238 差异(后缀数组+单调栈)


    题目链接

    解题思路

      前面的部分可以直接求,关键是如何快速求后半部分。
      我们知道两个后缀之间的lcp即他们之间的height数组的最小值。由于题目是对不同的数对计数,我们可以把后缀按照rk重新排序,每次计算当前的后缀j与它前面的后缀i的lcp,不难发现从j到i的过程中对height取min,得到的最小值是一块一块的。
      我们可以用单调栈算出每一块以当前下标j为右端点,以height[j]为区间最小值能扩展到的最小左端点l[j],然后用dp的思想就能根据l[j]递推出当前的后缀与排名在他前面的后缀的lcp之和。

    代码

    const int maxn = 1e6+10;
    int n, m;
    char s[maxn];
    int sa[maxn], x[maxn], y[maxn], c[maxn], rk[maxn], h[maxn];
    void get_sa() {
        for (int i = 1; i<=n; ++i) ++c[x[i]=s[i]];
        for (int i = 2; i<=m; ++i) c[i] += c[i-1];
        for (int i = n; i; --i) sa[c[x[i]]--] = i;
        for (int k = 1; k<=n; k<<=1) {
            int num = 0;
            for (int i = n-k+1; i<=n; ++i) y[++num] = i;
            for (int i = 1; i<=n; ++i)
                if (sa[i]>k) y[++num] = sa[i]-k;
            for (int i = 1; i<=m; ++i) c[i] = 0;
            for (int i = 1; i<=n; ++i) ++c[x[i]];
            for (int i = 1; i<=m; ++i) c[i] += c[i-1];
            for (int i = n; i; --i) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
            swap(x, y);
            x[sa[1]] = 1, num = 1;
            for (int i = 2; i<=n; ++i) 
                x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
            if (num==n) break;
            m = num;
        }
    }
    void get_h() {
        for (int i = 1; i<=n; ++i) rk[sa[i]] = i;
        for (int i = 1, k = 0; i<=n; ++i) {
            if (rk[i]==1) continue;
            if (k) --k;
            int j = sa[rk[i]-1];
            while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) ++k;
            h[rk[i]] = k;
        }
    }
    stack<int> sk;
    int l[maxn]; ll lcp[maxn];
    int main() { 
        IOS;
        cin >> s+1;
        n = strlen(s+1); m = 122;
        get_sa(); get_h();
        h[0] = -1;
        for (int i = n; i>=0; --i) {
            while(!sk.empty() && h[sk.top()]>h[i]) {
                l[sk.top()] = i;
                sk.pop();
            }
            sk.push(i);
        }
        ll sum = 0;
        for (int i = 1; i<=n; ++i) {
            lcp[i] = lcp[l[i]]+2LL*(i-l[i])*h[i];
            sum += 1LL*(n-1)*(n-i+1)-lcp[i];
        }
        cout << sum << endl;
        return 0;   
    } 
    
  • 相关阅读:
    idea中yml文件变成text样式并且没有提示
    挂载redhat镜像创建本地yum源
    Windows环境下Oracle数据库的自动备份脚本
    Oracle存储过程锁表
    DDL和客户端ip监控
    Linux 单实例oracle安装步骤
    Linux常用命令
    Linux常用目录
    oracle基础知识及语法
    Linux下Oracle新建用户并且将已有的数据dmp文件导入到新建的用户下的操作流程
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/15598905.html
Copyright © 2020-2023  润新知