首页 > ACM题库 > HDU-杭电 > HDU 2890-Longest Repeated subsequence-后缀数组-[解题报告]HOJ
2014
02-17

HDU 2890-Longest Repeated subsequence-后缀数组-[解题报告]HOJ

Longest Repeated subsequence

问题描述 :

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.

输入:

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.

输出:

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.

样例输入:

1
8 2
1
2
3
2
3
2
3
1

样例输出:

2
2
3

题目链接: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中序列排序一下,这样就保证了处理的时候就是从左往右了。

#include <iostream>
 #include <cstdio>
 #include <cmath>
 #include <algorithm>
 #include <cstring>
 using namespace std;
 const int maxn=50050;
 char str[maxn];
 int  num[maxn];
 int sa[maxn]; ///(你排第几)下标:排名情况, 数组值:首字符序号
 int rank[maxn];/// (排第几的是谁)  下标:首字符序号, 数组值:排名情况
 int height[maxn]; /// height[i]表示后缀i和后缀i-1的最长公共前缀
 int wa[maxn], wb[maxn], wv[maxn], wd[maxn];
 int X[maxn],  que[maxn];
 int pos;
 
 
 int cmp(int *r, int a, int b, int l)
 {
     return r[a]==r[b]&&r[a+l]==r[b+l];
 }
 
 void da(int *r, int n, int m){          ///  倍增算法 r为待匹配数组  n为总长度 m为字符范围
     int i, j, p, *x = wa, *y = wb, *t;
     for(i = 0; i < m; i ++) wd[i] = 0;
     for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
     for(i = 1; i < m; i ++) wd[i] += wd[i-1];
     for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;
     for(j = 1, p = 1; p < n; j *= 2, m = p){
         for(p = 0, i = n-j; i < n; i ++) y[p ++] = i;
         for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j;
         for(i = 0; i < n; i ++) wv[i] = x[y[i]];
         for(i = 0; i < m; i ++) wd[i] = 0;
         for(i = 0; i < n; i ++) wd[wv[i]] ++;
         for(i = 1; i < m; i ++) wd[i] += wd[i-1];
         for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];
         for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++){
             x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
         }
     }
 }
 
 void calHeight(int *r, int n){           ///  求height数组。
     int i, j, k = 0;
     for(i = 1; i <= n; i ++) rank[sa[i]] = i;
     for(i = 0; i < n; height[rank[i ++]] = k){
         for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
     }
 }
 
 int find(int tmp, int n)
 {
     int l=0, r=n, mid;
     while(l<=r)
     {
         mid=(l+r)>>1;
         if(X[mid]==tmp) return mid;
         else if(X[mid]<tmp) l=mid+1;
         else r=mid-1;
     }
 }
 
 bool judge(int mid, int rear, int k)
 {
     sort(que,que+rear);
     int pre=que[0], cnt=1;
     for(int i=1; i<rear; i++)
         if(que[i]-pre>=mid) pre=que[i], cnt++;
     return cnt>=k;
 }
 
 bool check(int mid, int n, int k)
 {
     int rear=0;
     for(int i=1; i<=n; i++)
     {
         if(height[i]<mid)
         {
             if(judge(mid,rear,k))
             {
                 pos=sa[i-1];
                 return true;
             }
             rear=0, que[rear++]=sa[i];
         }
         else que[rear++]=sa[i];
     }
     if(judge(mid,rear,k))
     {
         pos=sa[n-1];
         return true;
     }
     return false;
 }
 
 int main()
 {
     int n, k, T;
     cin >> T;
     while(T--)
     {
         scanf("%d%d",&n,&k);
         for(int i=0; i<n; i++) scanf("%d",num+i), X[i]=num[i];
         sort(X,X+n);
         int ep=0;
         for(int i=1; i<n; i++)
             if(X[i]!=X[ep]) X[++ep]=X[i];
         for(int i=0; i<n; i++)
             num[i]=find(num[i],ep)+2;
         num[n]=0;
         da(num,n+1,n+5);
         calHeight(num,n);
         int l=1, r=n, mid, ans=0;
         while(l<=r)
         {
             mid=(l+r)>>1;
             if(check(mid,n,k))
             {
                 l=mid+1;
                 ans=mid;
             }
             else r=mid-1;
         }
         printf("%d\n",ans);
         for(int i=pos; i<pos+ans; i++) printf("%d\n",X[num[i]-2]);
         if(T) puts("");
     }
     return 0;
 }

 

 

解题参考:http://www.cnblogs.com/kane0526/archive/2013/04/21/3033924.html


  1. 嗯 分析得很到位,确实用模板编程能让面试官对你的印象更好。在设置辅助栈的时候可以这样:push时,比较要push的elem和辅助栈的栈顶,elem<=min.top(),则min.push(elem).否则只要push(elem)就好。在pop的时候,比较stack.top()与min.top(),if(stack.top()<=min.top()),则{stack.pop();min.pop();},否则{stack.pop();}.

  2. 第二个方法挺不错。NewHead代表新的头节点,通过递归找到最后一个节点之后,就把这个节点赋给NewHead,然后一直返回返回,中途这个值是没有变化的,一边返回一边把相应的指针方向颠倒,最后结束时返回新的头节点到主函数。

  3. 5.1处,反了;“上一个操作符的优先级比操作符ch的优先级大,或栈是空的就入栈。”如代码所述,应为“上一个操作符的优先级比操作符ch的优先级小,或栈是空的就入栈。”

  4. 第23行:
    hash = -1是否应该改成hash[s ] = -1

    因为是要把从字符串s的start位到当前位在hash中重置

    修改提交后能accept,但是不修改居然也能accept

  5. #include <cstdio>
    #include <algorithm>

    struct LWPair{
    int l,w;
    };

    int main() {
    //freopen("input.txt","r",stdin);
    const int MAXSIZE=5000, MAXVAL=10000;
    LWPair sticks[MAXSIZE];
    int store[MAXSIZE];
    int ncase, nstick, length,width, tmp, time, i,j;
    if(scanf("%d",&ncase)!=1) return -1;
    while(ncase– && scanf("%d",&nstick)==1) {
    for(i=0;i<nstick;++i) scanf("%d%d",&sticks .l,&sticks .w);
    std::sort(sticks,sticks+nstick,[](const LWPair &lhs, const LWPair &rhs) { return lhs.l>rhs.l || lhs.l==rhs.l && lhs.w>rhs.w; });
    for(time=-1,i=0;i<nstick;++i) {
    tmp=sticks .w;
    for(j=time;j>=0 && store >=tmp;–j) ; // search from right to left
    if(j==time) { store[++time]=tmp; }
    else { store[j+1]=tmp; }
    }
    printf("%dn",time+1);
    }
    return 0;
    }