zoukankan      html  css  js  c++  java
  • 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;   
    } 
    
  • 相关阅读:
    转:C++中Static作用和使用方法
    转:C/C++中,空数组、空类、类中空数组的解析及其作用
    转:c++类实例在内存中的分配
    转:union 联合体(共用体)
    转:内存对齐与补齐 字节对齐与结构体大小
    转:c++内存分配
    转:代码重构
    转:设计模式六大原则(3):依赖倒置原则
    读书
    转:Teach Yourself Programming in Ten Years——用十年教会自己编程
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/15598905.html
Copyright © 2011-2022 走看看