zoukankan      html  css  js  c++  java
  • bzoj4453

    单调栈+set+后缀数组

    一道奇妙的题

    这道题如果对于每个询问$r$是固定的,那么就很简单了,可惜并不是

    由于r会变化,那么对于两个子串$[i...r],[j...r]$,他们的大小关系随着r的变化也会变化,使得我们不能直接预处理答案

    所以我们把询问离线,把每个询问按照r分类,通过考虑r的变化来完成询问

    对于两个后缀$[i...r],[j...r],(i<j)$,设他们的$lcp=l$,那么当$l∈[j,j+l-1]$时,$[i...r]>[j...r]$,当$r=j+l$时,则变成$[i...r]<[j...r]$,因为$s[i+l]<s[j+l]$(假设)

    我们称这样的两个后缀为$i$伴随$j$。

    那么我们对答案维护一个set,里面存着一些后缀,保证后缀当前的大小单调递减,这样我们就可以每次快速询问答案。

    但是由于$r$的变化每次大小关系会改变,也就是我们要把一些后缀删掉

    那么我们再维护一个单调栈,这里存的是严格的后缀大小,这个单调栈是为了给每个新加入的后缀求出哪些后缀伴随他

    那么如果当前的栈顶是大于新的后缀,$break$,否则标记$top$伴随$i$,并且记录当$i$,也就是$r=i+lcp$时删除栈顶

    每次删除一个元素时,伴随他的元素也应该删除,所以每次$dfs$删除即可,并在$set$里删除

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    #include <vector>
    using namespace std;
    const int N = 1e5 + 5;
    int n, k, m, top;
    int rnk[N], tmp[N], sa[N], ans[N], vis[N], st[N], h[N][21], Log[N];
    char s[N];
    set<int> S;
    vector<pair<int, int> > v[N];
    vector<int> del[N], g[N];
    bool cmp(int i, int j) {
        if(rnk[i] != rnk[j]) {
            return rnk[i] < rnk[j];
        }
        int ri = i + k <= n ? rnk[i + k] : -1;
        int rj = j + k <= n ? rnk[j + k] : -1;
        return ri < rj;
    }
    void dfs(int u) {
        vis[u] = 1;
        S.erase(u);
        for(int i = 0; i < g[u].size(); ++i) {
            if(!vis[g[u][i]]) {
                dfs(g[u][i]);
            }
        }
    }
    void SA() {
        for(int i = 1; i <= n; ++i) {
            sa[i] = i;
            rnk[i] = s[i];
        }
        for(k = 1; k <= n; k <<= 1) {
            sort(sa + 1, sa + n + 1, cmp);
            tmp[sa[1]] = 1;
            for(int i = 2; i <= n; ++i) {
                tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]));
            }
            for(int i = 1; i <= n; ++i) {
                rnk[i] = tmp[i];
            }
        }
        for(int i = 1; i <= n; ++i) {
            rnk[sa[i]] = i;
        }
        int d = 0;
        for(int i = 1; i <= n; ++i) {
            if(rnk[i] <= 1) {
                continue;
            }
            int j = sa[rnk[i] - 1];
            if(d) {
                --d;
            }
            for(; i + d <= n && j + d <= n; ++d) {
                if(s[i + d] != s[j + d]) {
                    break;
                }
            }
            h[rnk[i] - 1][0] = d;
        }
        for(int j = 1; j <= 20; ++j) {
            for(int i = 1; i + (1 << j) - 1 <= n; ++i) {
                h[i][j] = min(h[i][j - 1], h[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int rmq(int l, int r) {
        if(l == r) {
            return n - l + 1;
        }
        l = rnk[l];
        r = rnk[r];
        if(l > r) {
            swap(l, r);
        } 
        --r;
        int x = Log[r - l + 1];
        return min(h[l][x], h[r - (1 << x) + 1][x]);
    }
    int main() {
        scanf("%s%d", s + 1, &m);
        n = strlen(s + 1);
        for(int i = 2; i <= n; ++i) {
            Log[i] = Log[i >> 1] + 1;
        }
        SA();
        for(int i = 1; i <= m; ++i) {
            int l, r;
            scanf("%d%d", &l, &r);
            v[r].push_back(make_pair(l, i));
        }
        for(int i = 1; i <= n; ++i) {
            S.insert(i);
            while(top) {
                int lcp = rmq(st[top], i);
                if(s[st[top] + lcp] > s[i + lcp]) {
                    break;
                }           
                del[i + lcp].push_back(st[top]);
                g[i].push_back(st[top]);
                --top;
            }
            st[++top] = i;      
            for(int j = 0; j < del[i].size(); ++j) {
                if(!vis[del[i][j]]) {
                    dfs(del[i][j]);
                }
            }
            for(int j = 0; j < v[i].size(); ++j) {
                pair<int, int> x = v[i][j];
                ans[x.second] = *S.lower_bound(x.first);
            }
        }
        for(int i = 1; i <= m; ++i) {
            printf("%d
    ", ans[i]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    C#Redis哈希Hashes
    C#Redis集合set
    C#Redis列表List
    C#Redis字符串
    入门redis
    C#/Net代码精简优化技巧
    单点登录在asp.net中的简单实现
    sql注入
    数据库sql优化
    常常忘记但是很重要的sql语句
  • 原文地址:https://www.cnblogs.com/19992147orz/p/8464219.html
Copyright © 2011-2022 走看看