• [POJ 1743]Musical Theme


    Description

    题库链接

    给定一个长度为 (n) 的字符串,求最长重复子串,这两个子串不能重叠。(题目模型需转换)

    (1leq nleq 20000)

    Solution

    先二分答案,把题目变成判定性问题:判断是否存在两个长度为 (k) 的子串是相同的,且不重叠。

    解决这个问题的关键还是利用 (height) 数组。把排序后的后缀分成若干组,其中每组的后缀之间的 (height) 值都不小于 (k)

    容易看出,有希望成为最长公共前缀不小于 (k) 的两个后缀一定在同一组。然后对于每组后缀,只须判断每个后缀的 (sa) 值的最大值和最小值之差是否不小于 (k) 。如果有一组满足,则说明存在,否则不存在。

    因为后缀是有序的,相邻的后缀间的 (LCP) 必定的极大的;接下来就找到每个组里后缀 (sa) 值最大和最小的,如果差值不小于 (k) 就成立,因为这样小下标的后缀沿着 (LCP) 下去走 (k) 步才不会盖到大下标的后缀。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 20000+5;
    
    int ch[N], n, m, x[N<<1], y[N<<1], c[N], sa[N], rk[N], height[N];
    
    void get() {
        for (int i = 1; i <= m; i++) c[i] = 0;
        for (int i = 1; i <= n; i++) c[x[i] = ch[i]]++;
        for (int i = 2; i <= m; i++) c[i] += c[i-1];
        for (int i = n; i >= 1; 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 = 2; i <= m; i++) c[i] += c[i-1];
            for (int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i];
            swap(x, y); x[sa[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 ((m = num) == n) break;
        }
        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 && ch[i+k] == ch[j+k]) ++k;
            height[rk[i]] = k;
        }
    }
    bool judge(int x) {
        for (int i = 2, maxn = sa[1], minn = sa[1]; i <= n; i++) {
            if (height[i] >= x) maxn = max(maxn, sa[i]), minn = min(minn, sa[i]);
            if (height[i] < x || i == n) {
                if (maxn-minn > x) return true;
                maxn = minn = sa[i];
            }
        }
        return false;
    }
    void work() {
        while (~scanf("%d", &n) && n) {
            m = 88*2;
            for (int i = 1; i <= n; i++) scanf("%d", &ch[i]); --n;
            for (int i = 1; i <= n; i++) ch[i] = ch[i+1]-ch[i]+88;
            get(); int L = 4, R = n, ans = -1;
            while (L <= R) {
                int mid = (L+R)>>1;
                if (judge(mid)) ans = mid, L = mid+1;
                else R = mid-1;
            }
            printf("%d
    ", ans+1);
        }
    }
    int main() {work(); return 0; }
  • 相关阅读:
    问题6-10
    7.19 1
    经济学人常见词汇清单
    英语广播原声听力100篇MP3及听力原文
    6.30.2018
    6.26
    6.26
    6.26
    6.25
    6.25
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/9265123.html
Copyright © 2020-2023  润新知