【HDU2890 Longest Repeated subsequence】 后缀数组之重复k次最长子序列(不可覆盖)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2890

题目大意:给你一个含n个数的序列,再给你一个k,让你求最少重复k次的最长子序列(子序列不能重叠)。

解题思路:先吐槽一下,题意不明,蛋疼许久。 

我可以这么理解 : 1、保证子序列重复次数cnt大于k的前提下,len为一个子序列长度,然后最长子序列最长,即cnt*len最大。

                          2、保证子序列重复次数cnt大于k的前提下,只需让子序列长度len最长即可。

我在理解1中挣扎了许久才发现我题目都理解错了,题目意思是理解2,擦擦擦。

          这题X值很大,先离散化处理一下。 以前写过一道最少重复k次子序列可相互覆盖的题目,这题是不可覆盖。所以这里要特殊处理一下,开始我用标记,后来发现处理的时候还是有点问题,后来改成贪心做,即多开一个que数组,记录sa[]值,每次遇见height<mid(枚举的长度)时,对que中序列排序一下,这样就保证了处理的时候就是从左往右了。

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cmath>
  4 #include <algorithm>
  5 #include <cstring>
  6 using namespace std;
  7 const int maxn=50050;
  8 char str[maxn];
  9 int  num[maxn];
 10 int sa[maxn]; ///(你排第几)下标:排名情况, 数组值:首字符序号
 11 int rank[maxn];/// (排第几的是谁)  下标:首字符序号, 数组值:排名情况
 12 int height[maxn]; /// height[i]表示后缀i和后缀i-1的最长公共前缀
 13 int wa[maxn], wb[maxn], wv[maxn], wd[maxn];
 14 int X[maxn],  que[maxn];
 15 int pos;
 16 
 17 
 18 int cmp(int *r, int a, int b, int l)
 19 {
 20     return r[a]==r[b]&&r[a+l]==r[b+l];
 21 }
 22 
 23 void da(int *r, int n, int m){          ///  倍增算法 r为待匹配数组  n为总长度 m为字符范围
 24     int i, j, p, *x = wa, *y = wb, *t;
 25     for(i = 0; i < m; i ++) wd[i] = 0;
 26     for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
 27     for(i = 1; i < m; i ++) wd[i] += wd[i-1];
 28     for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;
 29     for(j = 1, p = 1; p < n; j *= 2, m = p){
 30         for(p = 0, i = n-j; i < n; i ++) y[p ++] = i;
 31         for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j;
 32         for(i = 0; i < n; i ++) wv[i] = x[y[i]];
 33         for(i = 0; i < m; i ++) wd[i] = 0;
 34         for(i = 0; i < n; i ++) wd[wv[i]] ++;
 35         for(i = 1; i < m; i ++) wd[i] += wd[i-1];
 36         for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];
 37         for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++){
 38             x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
 39         }
 40     }
 41 }
 42 
 43 void calHeight(int *r, int n){           ///  求height数组。
 44     int i, j, k = 0;
 45     for(i = 1; i <= n; i ++) rank[sa[i]] = i;
 46     for(i = 0; i < n; height[rank[i ++]] = k){
 47         for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
 48     }
 49 }
 50 
 51 int find(int tmp, int n)
 52 {
 53     int l=0, r=n, mid;
 54     while(l<=r)
 55     {
 56         mid=(l+r)>>1;
 57         if(X[mid]==tmp) return mid;
 58         else if(X[mid]<tmp) l=mid+1;
 59         else r=mid-1;
 60     }
 61 }
 62 
 63 bool judge(int mid, int rear, int k)
 64 {
 65     sort(que,que+rear);
 66     int pre=que[0], cnt=1;
 67     for(int i=1; i<rear; i++)
 68         if(que[i]-pre>=mid) pre=que[i], cnt++;
 69     return cnt>=k;
 70 }
 71 
 72 bool check(int mid, int n, int k)
 73 {
 74     int rear=0;
 75     for(int i=1; i<=n; i++)
 76     {
 77         if(height[i]<mid)
 78         {
 79             if(judge(mid,rear,k))
 80             {
 81                 pos=sa[i-1];
 82                 return true;
 83             }
 84             rear=0, que[rear++]=sa[i];
 85         }
 86         else que[rear++]=sa[i];
 87     }
 88     if(judge(mid,rear,k))
 89     {
 90         pos=sa[n-1];
 91         return true;
 92     }
 93     return false;
 94 }
 95 
 96 int main()
 97 {
 98     int n, k, T;
 99     cin >> T;
100     while(T--)
101     {
102         scanf("%d%d",&n,&k);
103         for(int i=0; i<n; i++) scanf("%d",num+i), X[i]=num[i];
104         sort(X,X+n);
105         int ep=0;
106         for(int i=1; i<n; i++)
107             if(X[i]!=X[ep]) X[++ep]=X[i];
108         for(int i=0; i<n; i++)
109             num[i]=find(num[i],ep)+2;
110         num[n]=0;
111         da(num,n+1,n+5);
112         calHeight(num,n);
113         int l=1, r=n, mid, ans=0;
114         while(l<=r)
115         {
116             mid=(l+r)>>1;
117             if(check(mid,n,k))
118             {
119                 l=mid+1;
120                 ans=mid;
121             }
122             else r=mid-1;
123         }
124         printf("%d\n",ans);
125         for(int i=pos; i<pos+ans; i++) printf("%d\n",X[num[i]-2]);
126         if(T) puts("");
127     }
128     return 0;
129 }
原文地址:https://www.cnblogs.com/kane0526/p/3033924.html