PKU 1743 Musical Theme 解题报告

果然是楼天成男人8题之一。。。。

  1. /*
  2.   Title: Musical Theme
  3.   Author: Jeff
  4.   Time:  2008/10/31
  5.   Complexity: O(N*log(N));
  6.   Result: 1100K 516MS/ AC
  7.   Reference: 
  8.         http://blog.csdn.net/milan_2008/archive/2008/09/15/2932293.aspx
  9.         http://203.208.39.99/search?q=cache:K84ULK6d-WoJ:hi.baidu.com/zfy0701/blog/item/f2278a0928991dca3bc763a0.html+pku+1743&hl=zh-CN&lr=lang_zh-CN|lang_zh-TW&newwindow=1&gl=cn&st_usg=ALhdy29Gp86u2sgbCS51uzMjjLxHnuq5IA&strip=1
  10.   Description: 
  11.         求最长不重叠差为定值子串的长度
  12.   Tips:
  13.         LCP问题,后缀数组。 
  14.         对于这道,要先把串转换一下,根据题目的性质,应该把输入得到的
  15.         串前后相减得到方便求解的新的串——设其为s,再求该串s中最长的
  16.         不重叠重复子串。由于不能重叠,导致height数组的最大值不一定是
  17.         解,因为相邻两串可能会重叠。
  18.         此题用后缀数组也有两种解法,1:二分枚举答案,2:用栈线性扫描,
  19.         主要说做法1,因为它更以理解些
  20.         现在我们假设-最长,不重叠,重复-子串的长度为len,那么在一个
  21.         height数组中有一些hgt[i]会小于len,在这个i左右的两个子串,他
  22.         们LCP是不可能大于或等于len的,这样,就可以吧height数组看做很
  23.         多LCP >= len的段,我们在每一段中进行扫描,记录这一段中最大和
  24.         最小的子串串索引(sa[x]),如果两者之和小于len,说明重叠了,
  25.         否则就找到了一个可行解。
  26.         易证:如果该串存在len1的不重叠子串,且len1 > len2,则该串也存
  27.         在长度len2的不重叠子串,解有连续性,所以我们在上面这个过程外我
  28.         们可以二分枚举解。
  29.         对了,二分查找之前之前可先调用查找len = 4是否成立,不成立就直
  30.         接return了。
  31.         这道题的关键点就是:判断解的时候对height数组进行分段处理,这个
  32.         分段的思想与其说重要,还不说就是height数组的基本性质。类似的分
  33.         段法在下题中再次用到。
  34.   p.s. 
  35. */
  36. #include <cstdio>
  37. #include <cstring>
  38. #include <algorithm>
  39. using namespace std;
  40. const int MAX = 20001;
  41. struct Sfx{
  42.     int idx;
  43.     int key[2];
  44.     bool operator<(const Sfx &other)const{
  45.         return (key[0] < other.key[0]) ||
  46.             (key[0] == other.key[0] && key[1] < other.key[1]);    
  47.     }    
  48. };
  49. int s[MAX];
  50. Sfx sa[MAX], temp_sa[MAX];
  51. int rank[MAX];
  52. int h[MAX];
  53. int hgt[MAX];
  54. inline int max(int a, int b){
  55.     return a > b ? a : b;    
  56. }
  57. inline int min(int a, int b){
  58.     return a < b ? a : b;
  59. }
  60. void cSort(Sfx *in, int key, int len, Sfx *out){
  61.     memset(h, 0, sizeof(h));
  62.     int *cnt = h;
  63.     for(int i = 0; i < len; i++)cnt[ in[i].key[key] ]++;
  64.     for(int i = 1; i < len; i++)cnt[i] += cnt[i-1];
  65.     for(int i = len - 1; i >= 0; i--)
  66.         out[ --cnt[in[i].key[key]] ] = in[i];
  67. }
  68. void build_sfx(int len){
  69.     for(int i = 0; i < len; i++){
  70.         sa[i].idx = sa[i].key[1] = i;
  71.         sa[i].key[0] = s[i];    
  72.     }    
  73.     sort(sa, sa + len);
  74.     for(int i = 0; i < len; i++)sa[i].key[1] = 0;
  75.     
  76.     int wid = 1;
  77.     while(wid < len){
  78.         //为0的时候,因为当所有的数都相同时,比较不出大小。 
  79.         //所以在所有数后面加了一个无穷小的数。 
  80.         rank[sa[0].idx] = 0;    
  81.         for(int i = 1; i < len; i++){
  82.             rank[sa[i].idx] = rank[sa[i-1].idx];
  83.             if(sa[i-1] < sa[i])
  84.                 rank[sa[i].idx]++;    
  85.         }
  86.         //printf("r: ");for(int i = 0; i < len; i++)printf("%d ", rank[i]);printf("/n");
  87.         for(int i = 0; i < len; i++){
  88.             sa[i].idx = i;
  89.             sa[i].key[0] = rank[i];    
  90.             //key[1]不能为-1, 因为cSort对大于等于0的数进行统计。 
  91.             sa[i].key[1] = (i + wid < len ? rank[i+wid] : 0); 
  92.         }
  93.         cSort(sa, 1, len, temp_sa); cSort(temp_sa, 0, len, sa);
  94.         //printf("sa:");for(int i = 0; i < len; i++)printf("%d ", sa[i].idx);printf("/n");
  95.         wid *= 2;
  96.     }
  97.     //需要再计算一次rank值,因为以上最后一次cSort后,rank的计算还差一次。 
  98.     rank[sa[0].idx] = 0;    
  99.     for(int i = 1; i < len; i++){
  100.         rank[sa[i].idx] = rank[sa[i-1].idx];
  101.         if(sa[i-1] < sa[i])rank[sa[i].idx]++;    
  102.     }
  103. }
  104. void calH(int len){
  105.     for(int i = 0; i < len; i++){
  106.         if(rank[i] == 0){
  107.             h[i] = 0;
  108.             hgt[rank[i]] = 0;
  109.         }
  110.         else{
  111.             int k;
  112.             if(i == 0 || h[i-1] < 1)k = 0;
  113.             else k = h[i-1] - 1;
  114.             for(int j = sa[rank[i]-1].idx; s[j + k] == s[i + k]; k++);
  115.             h[i] = k;
  116.             hgt[rank[i]] = k;
  117.         }    
  118.     }    
  119. }
  120. bool check(int mid, int len){
  121.     int ma = 0, mi = MAX;
  122.     for(int i = 1; i < len; i++){
  123.         if(hgt[i] < mid){
  124.             ma = 0;
  125.             mi = MAX;
  126.         }else{
  127.             int minl = min(sa[i].idx, sa[i-1].idx);
  128.             int maxr = max(sa[i].idx, sa[i-1].idx);
  129.             mi = min(minl, mi);
  130.             ma = max(maxr, ma);
  131.             if(ma - mi >= mid)return true;    
  132.         }
  133.     }   
  134.     return false;
  135. }
  136. int main(){
  137.     freopen("in.txt""r", stdin);
  138.     freopen("out.txt""w", stdout);
  139.     int N;
  140.     while(scanf("%d", &N) && N != 0){
  141.         int now, pre;
  142.         scanf("%d", &pre);
  143.         for(int i = 0; i < N - 1; i++){
  144.             scanf("%d", &now);
  145.             s[i] = now - pre + 100; //计算前后数值的差,最后求其实就是长度>=4 
  146.             pre = now;    
  147.         }
  148.         //末尾结束符 
  149.         s[N-1] = -MAX;
  150.         build_sfx(N);
  151.         calH(N);
  152.         //printf("r: ");for(int i = 0; i < N; i++)printf("%d ", rank[i]);printf("/n");
  153.         //printf("sa:");for(int i = 0; i < N; i++)printf("%d ", sa[i].idx);printf("/n");
  154.         //printf("h: ");for(int i = 0; i < N; i++)printf("%d ", h[i]);printf("/n");
  155.         //printf("hgt:");for(int i = 0; i < N; i++)printf("%d ", hgt[i]);printf("/n");
  156.         int ans = -1;
  157.         int l = 4, r = N/2+1;
  158.         int mid;
  159.         while(l <= r){
  160.             mid = (l + r) / 2;
  161.             if(check(mid, N-1)){
  162.                 l = mid + 1;
  163.                 ans = mid;
  164.             }    
  165.             else
  166.                 r = mid - 1;
  167.         }
  168.         printf("%d/n", ans+1);
  169.     }
  170.     return 0;    
  171. }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值