zoukankan      html  css  js  c++  java
  • poj Musical Theme 后缀数组 二分

    题目中认为如果两段对应位音调变动幅度相同,则两端相似。求最长的不相交,且至少相隔1格,的相似段落的长度。那么我们考虑对原序列,求出两位之前的差。这样子问题就转化成了,求两端相同的最长且不相交,且至少相隔1个,的子序列了,是经典的后缀数组问题。

    我们考虑先二分答案长度k,把问题转化为判断性问题。

    然后把所有后缀进行分组,保证组内的后缀的公共前缀长度不小于k。具体分组方式,因为我们知道字典序相近的后缀的可能前缀最长,所以我们按照从height[1]遍历,height[i]遍历到height[n - 1]。每一个height[i]都表示,两个字典序相邻的后缀,即rank[i]的后缀和rank[i + 1]的后缀的最长公共前缀长度,那么一段连续x个均不小于k的height即为,x+1个拥有长度公共前缀都不小于k的后缀。我们我们只要看这个分组里的所有后缀的最早开始下标和最晚开始下标,的差值有没有大于k。如果有,则证明存在两个后缀,存在长度为k的公共前缀,并且不相交,且至少相隔1格。

      1 #include <cstdio>
      2 #include <algorithm>
      3 using namespace std;
      4 const int MAXN = 101000;
      5 int c[MAXN],sa[MAXN],rank[MAXN],height[MAXN],tp[MAXN],vec[MAXN];
      6 int n,m;
      7 void qsort()
      8 {
      9     for (int i = 0;i <= m;i++)
     10         c[i] = 0;
     11     for (int i = 1;i <= n;i++)
     12         c[rank[i]]++;
     13     for (int i = 1;i <= m;i++)
     14         c[i] += c[i - 1];
     15     for (int i = n;i >= 1;i--)
     16         sa[c[rank[tp[i]]]--] = tp[i];
     17 }
     18 void suffixsort()
     19 {
     20     m = 200;
     21     for (int i = 1;i <= n;i++)
     22     {
     23         rank[i] = vec[i];
     24         tp[i] = i;
     25     }
     26     qsort();
     27     for (int k = 1,p = 0;p < n;m = p,k <<= 1)
     28     {
     29         p = 0;
     30         for (int i = 1;i <= k;i++)
     31             tp[++p] = n - k + i;
     32         for (int i = 1;i <= n;i++)
     33             if (sa[i] > k)
     34                 tp[++p] = sa[i] - k;
     35         qsort();
     36         swap(tp,rank);
     37         rank[sa[1]] = p = 1;
     38         for (int i = 2;i <= n;i++)
     39             rank[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + k] == tp[sa[i] + k]) ? p : ++p; 
     40     }
     41 }
     42 int solve(int x,int y)
     43 {
     44     int res = 0;
     45     while (vec[x++] == vec[y++]) 
     46         res++;
     47     return res;
     48 }
     49 void get_height()
     50 {
     51     int cur = 0;
     52     for (int i = 1;i <= n;i++)
     53     {
     54         if (cur != 0)
     55             cur--;
     56         height[rank[i]] = cur = cur + solve(i + cur,sa[rank[i] + 1] + cur);
     57     }
     58 }
     59 bool check(int k)
     60 {
     61     int maxn = 0,minn = 100000000;
     62     for (int i = 1;i <= n - 1;i++)
     63         if (height[i] >= k)
     64         {
     65             minn = min(minn,min(sa[i],sa[i + 1]));
     66             maxn = max(maxn,max(sa[i],sa[i + 1]));
     67             if (maxn - minn > k)
     68                 return true;
     69         }else
     70         {
     71             maxn = 0;
     72             minn = 100000000;
     73         }
     74     return false;
     75 }
     76 int main()
     77 {
     78     while (scanf("%d",&n) && n > 0)
     79     {
     80         for (int i = 1;i <= n;i++)
     81             scanf("%d",&vec[i]);
     82         n--;
     83         for (int i = 1;i <= n;i++)
     84             vec[i] = vec[i + 1] - vec[i] + 88;//保证非负
     85         vec[n + 1] = 0;
     86         suffixsort();
     87         get_height();
     88         int l = 0,r = n >> 1;
     89         while (l < r)
     90         {
     91             int mid = l + r + 1 >> 1;
     92             if (check(mid))    
     93                 l = mid;
     94             else
     95                 r = mid - 1;
     96         }
     97         if (l >= 4)
     98             printf("%d
    ",l + 1);
     99         else
    100             printf("%d
    ",0);
    101     }
    102     return 0;
    103 }
    心之所动 且就随缘去吧
  • 相关阅读:
    Java之hashCode的作用和equals方法的重构规则
    Java-IO流之File操作和Properties操作
    Java-IO流之转换流的使用和编码与解码原理
    Java-IO 字节流的使用和效率比较
    Java-IO流之BufferedReader 和BufferedWriter的使用和原理
    二分查找
    leetcode530 二叉搜索树的最小绝对差
    leetcode94 二叉树的中序遍历
    leetcode17电话号码字母组合
    leetcode26 删除排序数组中的重复项
  • 原文地址:https://www.cnblogs.com/iat14/p/10701740.html
Copyright © 2011-2022 走看看