HDU - 2890 Longest Repeated subsequence 后缀数组+二分+贪心、不可重叠子串、离散化

129 篇文章 0 订阅
58 篇文章 0 订阅

Longest Repeated subsequence

  HDU - 2890


Write a program that takes a sequence of number and returns length of the longest repeated 
subsequence. A repeated subsequence which repeats identically at least K times without overlaps. 
For example 1 2 3 1 2 3 2 3 1 repeats 2 3 for three times. 
Input
Line 1: The input contains a single integer T , the number of test cases. 
Line 2: Two space-separated integers: N and K (1 <= n <= 50000), (2 <= k <= n) 
Lines 3..N+2: N integers, one per line. The integer is between 0 and 10^9. 
It is guaranteed that at least one subsequence is repeated at least K times. 
Output
For each test case, the first line print the length n of the Longest Repeated Subsequence and the line 2…n+1 print the subsequence numbers.(if we have several subsequence with the same length output the lexicographic order smallest one). Separate output for adjacent cases with a single blank line.
Sample Input
1
8 2
1
2
3
2
3
2
3
1
Sample Output
2
2
3

Source HDU - 2890
My Solution
题意:给出一个序列,要求找出出现过至少m次的最长(连续)子序列,求其最长长度和具体字典序最小的序列,且不能有重叠。 后缀数组+二分+贪心
先离散化,然后跑出后缀数组sa和height数组,然后二分答案,检查的时候,维护height[i] > mid 的sa[i]值及个数,日光个数大于等于m, 则把这段区间的sa[i],排序,然后从小的开始贪,相邻的2个,如果间隔小于mid,则去掉大的比去掉小的sa[i]更好,然后讨论下一个, 最后判断剩余的sa[i]的两两间隔不小于mid的sa[i]的个数大于等于m,则这就是答案,要求字典序最小所以check的时候碰到第一个满足的答案就可以return了。 复杂度 O(nlognlogn)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 5e4 + 8;
int sa[maxn], height[maxn];
int _rank[maxn], t1[maxn], t2[maxn], c[maxn];
int s[maxn];
inline void get_sa(const int &n, int m)
{
    int i, k, *x = t1, *y = t2, p, j;
    for(i = 0; i < m; i++) c[i] = 0;
    for(i = 0; i < n; i++) ++ c[x[i] = s[i]];
    for(i = 1; i < m; i++) c[i] += c[i - 1];
    for(i = n - 1; i >= 0; i--) sa[-- c[x[i]]] = i;
    for(k = 1; k <= n; k <<= 1){
        p = 0;
        for(i = n - k; i < n; i++) y[p ++] = i;
        for(i = 0; i < n; i++) if(sa[i] >= k) y[p ++] = sa[i] - k;
        for(i = 0; i < m; i++) c[i] = 0;
        for(i = 0; i < n; i++) ++ c[x[y[i]]];
        for(i = 1; i < m; i++) c[i] += c[i - 1];
        for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
        swap(x, y), p = 1, x[sa[0]] = 0;
        for(i = 1; i < n; i++)
            x[sa[i]] = (y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k]) ? p - 1 : p ++;
        if(p >= n) break;
        m = p;
    }
    k = 0;
    for(i = 0; i < n; i++) _rank[sa[i]] = i;
    for(i = 0; i < n; i++){
        if(k) --k; if(!_rank[i]) continue;
        j = sa[_rank[i] - 1];
        while(s[i + k] == s[j + k]) k++;
        height[_rank[i]] = k;
    }
}
int val[maxn];
inline void print(const int &n)
{
    for(int i = 1; i <= n; i++){
        //cout << i << " : " << height[i] << " " << sa[i] << endl;
        for(int j = sa[i]; j < n; j++){
            cout << val[j];
        }
        cout << endl;
    }
    cout << endl;
}

map<int, int> Ind;
vector<int> vec;
int start, t, m, n, cnt;
inline bool check(const int &x)
{
    cnt = 1; t = 1; vec.push_back(sa[1]);
    for(int i = 2, j, sz, res; i <= n; i++){
        if(height[i] >= x) {cnt++; vec.push_back(sa[i]);}
        else{cnt = 1; t = i; vec.clear(); vec.push_back(sa[i]);}
        if(cnt >= m){
            if(i == n || height[i+1] < x){
                sort(vec.begin(), vec.end());
                sz = vec.size(); res = vec[0];
                for(j = 1; j < sz; j++){
                    if(vec[j] - res < x) cnt--;
                    else res = vec[j];
                }
                if(cnt >= m){ start = t; return true;}
            }

        }
    }
    return false;
}

int main()
{
    #ifdef LOCAL
    freopen("8.in", "r", stdin);
    //freopen("8.out", "w", stdout);
    #endif // LOCAL
    ios::sync_with_stdio(false); cin.tie(0);

    int T, sz, i, ansa, ansb, l, r, mid;
    cin >> T;
    while(T--){
        cin >> n >> m;
        vec.clear(); Ind.clear(); memset(s, 0, sizeof s);
        for(i = 0; i < n; i++){
            cin >> val[i];
            vec.push_back(val[i]);
        }
        if(m == 1){cout << n << "\n"; for(i = 0; i < n; i++) cout << val[i] << "\n"; if(T) cout << "\n"; continue;}
        sort(vec.begin(), vec.end());
        vec.erase(unique(vec.begin(), vec.end()), vec.end());
        sz = vec.size();
        for(i = 0; i < sz; i++){
            Ind[vec[i]] = i + 1;
            //cout << vec[i] << " " << i + 1 << endl;
        }
        for(i = 0; i < n; i++) s[i] = Ind[val[i]];
        get_sa(n+1, sz+8);
        //print(n);
        ansa = ansb = 0; l = 0, r = n + 1;
        while(l + 1 < r){
            mid = (l + r) >> 1; vec.clear();
            if(check(mid)){ansa = mid; ansb = start; l = mid; }
            else r = mid;
        }
        cout << ansa << "\n";
        for(i = sa[ansb]; i < sa[ansb] + ansa; i++) cout << val[i] << "\n";
        if(T) cout << "\n";
    }
    return 0;
}



Thank you                                                                                                                                              ------from  ProLights

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值