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\n",l + 1); 99 else 100 printf("%d\n",0); 101 } 102 return 0; 103 }
心之所动 且就随缘去吧