zoukankan      html  css  js  c++  java
  • BZOJ 4556: [Tjoi2016&Heoi2016]字符串(后缀数组 + 二分答案 + 主席树 + ST表 or 后缀数组 + 暴力)

    题意

    一个长为 (n) 的字符串 (s),和 (m) 个询问。每次询问有 (4) 个参数分别为 (a,b,c,d)

    要你告诉它 (s[a...b]) 中的所有子串 和 (s[c...d]) 的 最长公共前缀 ((mathrm{LCP})) 的最大值。

    ((1le n,mle 10^5, ale b,cle d,1le a,b,c,dle n))

    题解

    一开始看错了题 以为是 ([a,b]) 中所有子串 和 ([c,d]) 中所有子串的 (mathrm{LCP}) 这怎么能做啊!!!

    仔细观察了一下 发现是 ([a,b]) 的所有子串 和 ([c,d]) 。。。。

    那么题目就变 简单 了一点。。。

    首先我们考虑与 ([c,d]) 有最长 (mathrm{LCP}) 的在哪里

    不难发现 就是后缀排序后 (rk[i])(rk[c]) 最靠近的 (i)

    那么我们可以转化求 ([a,b]) 中的这个 (i) 就行了qwq

    答案表示出来大概是这样子的。

    [displaystyle mathrm{ans}=min(d-c+1,max_{i=a}^{b} {min(mathrm{LCP}(i, c),b-i+1)}) ]

    我们发现 直接求这个 (i) 会被后面的 (b-i+1) 限制掉 所以不能直接这样求

    但我们可以考虑转化一下 我们考虑 二分答案 如果判断一个答案是否存在就容易一些了

    我们考虑二分这个长度 假设是 (len) 那么前面的 (i) 就只能存在于 ([a,b-len+1]) 这个区间内

    然后看 (rk[c]) 周围连续的 (height[q]ge len) 可以延伸到哪个范围 这个东西 直接用 (mathrm{ST}) 可以实现

    怎么实现呢 类似于倍增的思想 就是把那段距离看成一串二进制 然后从高到低去消掉一个个数就行了

    得到这个区间 ([sl,sr]) 后 我们就需要查找里面是否存在 ([a,b-len+1]) 的元素 这个东西就直接上 主席树 就行了

    最后时间复杂度就是 (O(n log^2 n)) 咯(令 (n,q) 同级)

    思路就很清晰了 但是代码就一点也不好写。。。 先挂出来吧。。

    代码

    /**************************************************************
        Problem: 4556
        User: zjp_shadow
        Language: C++
        Result: Time_Limit_Exceed
    ****************************************************************/
     
    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    using namespace std;
     
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
     
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * fh;
    }
     
    void File() {
    #ifdef zjp_shadow
        freopen ("4556.in", "r", stdin);
        freopen ("4556.out", "w", stdout);
    #endif
    }
     
    const int N = 2e6 + 1e3;
    struct Suffix_Array {
        int sa[N], tmp[N], rk[N], n, m, c[N];
        char str[N];
     
        void Init(int n, char bas[]) { this -> n = n; Cpy(str, bas); }
     
        inline void Radix_Sort() {
            For (i, 1, m) c[i] = 0;
            For (i, 1, n) ++ c[rk[i]];
            For (i, 1, m) c[i] += c[i - 1];
            Fordown (i, n, 1) sa[c[rk[tmp[i]]] --] = tmp[i];
        }
     
        inline void Build_Sa() {
            For (i, 1, n) rk[i] = str[i], tmp[i] = i;
            m = 255; Radix_Sort();
            for (register int k = 1, p; k <= n; k <<= 1) {
                p = 0;
                For (i, n - k + 1, n) tmp[++ p] = i;
                For (i, 1, n) if (sa[i] > k) tmp[++ p] = sa[i] - k;
                Radix_Sort(); swap(rk, tmp);
                rk[sa[1]] = 1, m = 1;
                For (i, 2, n) rk[sa[i]] = (tmp[sa[i]] == tmp[sa[i - 1]] && tmp[sa[i] + k] == tmp[sa[i - 1] + k]) ? m : ++ m;
                if (m >= n) return ;
            }
        }
     
        int height[N];
        inline void Get_Height() {
            for (int i = 1, j, k = 0; i <= n; ++ i) {
                if (k) -- k;
                j = sa[rk[i] - 1];
                while (str[i + k] == str[j + k]) ++ k;
                height[rk[i]] = k;
            }
        }
    } SA;
     
    struct Chairman_Tree {
        int ls[N], rs[N], tot[N], Size, rt[N];
     
        void Insert(int &o, int pre, int l, int r, int up) {
            o = ++ Size; ls[o] = ls[pre]; rs[o] = rs[pre];
            tot[o] = tot[pre] + 1; if (l == r) return;
            int mid = (l + r) >> 1;
            if (up <= mid) Insert(ls[o], ls[pre], l, mid, up);
            else Insert(rs[o], rs[pre], mid + 1, r, up);
        }
     
        bool Query(int s, int t, int l, int r, int ql, int qr) {
            int now = tot[t] - tot[s];
            if (!now) return false;
            if (ql <= l && r <= qr) return true;
            int mid = (l + r) >> 1;
            if (ql <= mid && Query(ls[s], ls[t], l, mid, ql, qr)) return true;
            if (qr > mid && Query(rs[s], rs[t], mid + 1, r, ql, qr)) return true;
            return false;
        }
    } CT;
     
    struct Sparse_Table {
        int minv[N][20], Log[N];
     
        void Build(int n, int a[]) {
            For (i, 1, n) 
                minv[i][0] = a[i], Log[i] = Log[i >> 1] + 1;
            For (j, 1, Log[n])
                For (i, 1, n - (1 << j) + 1) 
                    minv[i][j] = min(minv[i][j - 1], minv[i + (1 << (j - 1))][j - 1]);
        }
    } ST1, ST2;
     
    int n, m;
     
    inline bool Check(int len, int a, int b, int c, int d) {
        int sl = SA.rk[c], sr = SA.rk[c];
        Fordown (i, ST2.Log[sl], 0) 
            if (ST2.minv[n - sl + 1][i] >= len) sl -= (1 << i);
        Fordown (i, ST1.Log[n - sr + 1], 0) 
            if (ST1.minv[sr + 1][i] >= len) sr += (1 << i);
        int ql = a, qr = b - len + 1;
        return CT.Query(CT.rt[ql - 1], CT.rt[qr], 1, n, sl, sr);
    }
     
    inline int Get_Ans(int a, int b, int c, int d) {
        int l = 1, r = min(b - a + 1, d - c + 1), ans = 0;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (Check(mid, a, b, c, d)) l = mid + 1, ans = mid;
            else r = mid - 1;
        }
        return ans;
    }
     
    int val1[N], val2[N];
    char str[N];
     
    int main () {
        File();
        n = read(); m = read();
        scanf ("%s", str + 1);
        SA.Init(n, str);
        SA.Build_Sa();
        SA.Get_Height();
     
        For (i, 1, n) {
            CT.Insert(CT.rt[i], CT.rt[i - 1], 1, n, SA.rk[i]);
            val1[i] = val2[n - i + 1] = SA.height[i];
        }
        ST1.Build(n, val1);
        ST2.Build(n, val2);
     
        For (i, 1, m) {
            int a = read(), b = read(), c = read(), d = read();
            printf ("%d
    ", Get_Ans(a, b, c, d));
        }
        //cerr << (double) clock() /CLOCKS_PER_SEC << endl;
        return 0;
    }
    

    彩蛋

    细心的读者肯定发现了 这个代码的 result(mathrm{TLE}) 2333

    为什么呢 本人常数巨大啊!!!

    但这份代码交到 (luogu) 上不开 (O2) 是 8000ms 开了是 4000ms

    我突然想看看别人怎么写的 然后查找一波最优解 诶 有个叫 yyb_test 的神犇 只要 400ms

    我仔细翻阅了一波大佬的代码 惊了 这不是暴力吗!!!

    这才有了我现在的标题 (后缀数组 + 暴力)

    我们继续考虑之前答案的那个式子

    [displaystyle mathrm{ans}=min(d-c+1,max_{i=a}^{b} {min(mathrm{LCP}(i, c),b-i+1)}) ]

    我们考虑向 (rk[c]) 前后去扫一下得到答案

    其中如果此处 (sa[i])([a,b]) 之中的话我们就计入答案就行了。

    然后就有一个史诗级优化 就是当前扫的 (height)(min) 值 如果不优于当前的 (mathrm{ans})

    我们就可以轻易退出循环啦 这由于数据较为随机 所以 (height) 就比较降的比较快 所以就比较快了qwq

    理论 时间复杂度 (O(nq)) 实际(随机数据) 时间复杂度 (O(frac{mathrm{std}}{10})) 23333

    放个对比图2333

    pic

    BruteForce 代码

    /**************************************************************
        Problem: 4556
        User: zjp_shadow
        Language: C++
        Result: Accepted
        Time:1768 ms
        Memory:59916 kb
    ****************************************************************/
     
    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    using namespace std;
     
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
     
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * fh;
    }
     
    void File() {
    #ifdef zjp_shadow
        freopen ("4556.in", "r", stdin);
        freopen ("4556.out", "w", stdout);
    #endif
    }
     
    const int N = 2e6 + 1e3;
    struct Suffix_Array {
        int sa[N], tmp[N], rk[N], n, m, c[N];
        char str[N];
     
        void Init(int n, char bas[]) { this -> n = n; Cpy(str, bas); }
     
        inline void Radix_Sort() {
            For (i, 1, m) c[i] = 0;
            For (i, 1, n) ++ c[rk[i]];
            For (i, 1, m) c[i] += c[i - 1];
            Fordown (i, n, 1) sa[c[rk[tmp[i]]] --] = tmp[i];
        }
     
        inline void Build_Sa() {
            For (i, 1, n) rk[i] = str[i], tmp[i] = i;
            m = 255; Radix_Sort();
            for (register int k = 1, p; k <= n; k <<= 1) {
                p = 0;
                For (i, n - k + 1, n) tmp[++ p] = i;
                For (i, 1, n) if (sa[i] > k) tmp[++ p] = sa[i] - k;
                Radix_Sort(); swap(rk, tmp);
                rk[sa[1]] = 1, m = 1;
                For (i, 2, n) rk[sa[i]] = (tmp[sa[i]] == tmp[sa[i - 1]] && tmp[sa[i] + k] == tmp[sa[i - 1] + k]) ? m : ++ m;
                if (m >= n) return ;
            }
        }
     
        int height[N];
        inline void Get_Height() {
            for (int i = 1, j, k = 0; i <= n; ++ i) {
                if (k) -- k;
                j = sa[rk[i] - 1];
                while (str[i + k] == str[j + k]) ++ k;
                height[rk[i]] = k;
            }
        }
    } SA;
     
    int n, m;
    inline int Get_Ans(int a, int b, int c, int d) {
        int ans = 0, len = min(b - a + 1, d - c + 1), tmp = len;
        Fordown (i, SA.rk[c], 1) {
            if (SA.sa[i] >= a && SA.sa[i] <= b)
                chkmax(ans, min(tmp, b - SA.sa[i] + 1));
            chkmin(tmp, SA.height[i]);
            if (tmp <= ans) break;
        }
        tmp = len;
        For (i, SA.rk[c] + 1, n) {
            chkmin(tmp, SA.height[i]);
            if (tmp <= ans) break;
            if (SA.sa[i] >= a && SA.sa[i] <= b)
                chkmax(ans, min(tmp, b - SA.sa[i] + 1));
        }
        return ans;
    }
     
    int val1[N], val2[N];
    char str[N];
    int main () {
        File();
        n = read(); m = read();
        scanf ("%s", str + 1);
        SA.Init(n, str);
        SA.Build_Sa();
        SA.Get_Height();
     
        For (i, 1, m) {
            int a = read(), b = read(), c = read(), d = read();
            printf ("%d
    ", Get_Ans(a, b, c, d));
        }
        //cerr << (double) clock() /CLOCKS_PER_SEC << endl;
        return 0;
    }
    
  • 相关阅读:
    (Good Bye 2019) Codeforces 1270B Interesting Subarray
    (Good Bye 2019) Codeforces 1270A Card Game
    Codeforces 1283D Christmas Trees(BFS)
    Codeforces 1283C Friends and Gifts
    Codeforces 1283B Candies Division
    1095 Cars on Campus (30)
    1080 Graduate Admission (30)
    1099 Build A Binary Search Tree (30)
    1018 Public Bike Management (30)
    1087 All Roads Lead to Rome (30)
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/8727385.html
Copyright © 2011-2022 走看看