果然是楼天成男人8题之一。。。。
- /*
- Title: Musical Theme
- Author: Jeff
- Time: 2008/10/31
- Complexity: O(N*log(N));
- Result: 1100K 516MS/ AC
- Reference:
- http://blog.csdn.net/milan_2008/archive/2008/09/15/2932293.aspx
- 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
- Description:
- 求最长不重叠差为定值子串的长度
- Tips:
- LCP问题,后缀数组。
- 对于这道,要先把串转换一下,根据题目的性质,应该把输入得到的
- 串前后相减得到方便求解的新的串——设其为s,再求该串s中最长的
- 不重叠重复子串。由于不能重叠,导致height数组的最大值不一定是
- 解,因为相邻两串可能会重叠。
- 此题用后缀数组也有两种解法,1:二分枚举答案,2:用栈线性扫描,
- 主要说做法1,因为它更以理解些
- 现在我们假设-最长,不重叠,重复-子串的长度为len,那么在一个
- height数组中有一些hgt[i]会小于len,在这个i左右的两个子串,他
- 们LCP是不可能大于或等于len的,这样,就可以吧height数组看做很
- 多LCP >= len的段,我们在每一段中进行扫描,记录这一段中最大和
- 最小的子串串索引(sa[x]),如果两者之和小于len,说明重叠了,
- 否则就找到了一个可行解。
- 易证:如果该串存在len1的不重叠子串,且len1 > len2,则该串也存
- 在长度len2的不重叠子串,解有连续性,所以我们在上面这个过程外我
- 们可以二分枚举解。
- 对了,二分查找之前之前可先调用查找len = 4是否成立,不成立就直
- 接return了。
- 这道题的关键点就是:判断解的时候对height数组进行分段处理,这个
- 分段的思想与其说重要,还不说就是height数组的基本性质。类似的分
- 段法在下题中再次用到。
- p.s.
- */
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- using namespace std;
- const int MAX = 20001;
- struct Sfx{
- int idx;
- int key[2];
- bool operator<(const Sfx &other)const{
- return (key[0] < other.key[0]) ||
- (key[0] == other.key[0] && key[1] < other.key[1]);
- }
- };
- int s[MAX];
- Sfx sa[MAX], temp_sa[MAX];
- int rank[MAX];
- int h[MAX];
- int hgt[MAX];
- inline int max(int a, int b){
- return a > b ? a : b;
- }
- inline int min(int a, int b){
- return a < b ? a : b;
- }
- void cSort(Sfx *in, int key, int len, Sfx *out){
- memset(h, 0, sizeof(h));
- int *cnt = h;
- for(int i = 0; i < len; i++)cnt[ in[i].key[key] ]++;
- for(int i = 1; i < len; i++)cnt[i] += cnt[i-1];
- for(int i = len - 1; i >= 0; i--)
- out[ --cnt[in[i].key[key]] ] = in[i];
- }
- void build_sfx(int len){
- for(int i = 0; i < len; i++){
- sa[i].idx = sa[i].key[1] = i;
- sa[i].key[0] = s[i];
- }
- sort(sa, sa + len);
- for(int i = 0; i < len; i++)sa[i].key[1] = 0;
- int wid = 1;
- while(wid < len){
- //为0的时候,因为当所有的数都相同时,比较不出大小。
- //所以在所有数后面加了一个无穷小的数。
- rank[sa[0].idx] = 0;
- for(int i = 1; i < len; i++){
- rank[sa[i].idx] = rank[sa[i-1].idx];
- if(sa[i-1] < sa[i])
- rank[sa[i].idx]++;
- }
- //printf("r: ");for(int i = 0; i < len; i++)printf("%d ", rank[i]);printf("/n");
- for(int i = 0; i < len; i++){
- sa[i].idx = i;
- sa[i].key[0] = rank[i];
- //key[1]不能为-1, 因为cSort对大于等于0的数进行统计。
- sa[i].key[1] = (i + wid < len ? rank[i+wid] : 0);
- }
- cSort(sa, 1, len, temp_sa); cSort(temp_sa, 0, len, sa);
- //printf("sa:");for(int i = 0; i < len; i++)printf("%d ", sa[i].idx);printf("/n");
- wid *= 2;
- }
- //需要再计算一次rank值,因为以上最后一次cSort后,rank的计算还差一次。
- rank[sa[0].idx] = 0;
- for(int i = 1; i < len; i++){
- rank[sa[i].idx] = rank[sa[i-1].idx];
- if(sa[i-1] < sa[i])rank[sa[i].idx]++;
- }
- }
- void calH(int len){
- for(int i = 0; i < len; i++){
- if(rank[i] == 0){
- h[i] = 0;
- hgt[rank[i]] = 0;
- }
- else{
- int k;
- if(i == 0 || h[i-1] < 1)k = 0;
- else k = h[i-1] - 1;
- for(int j = sa[rank[i]-1].idx; s[j + k] == s[i + k]; k++);
- h[i] = k;
- hgt[rank[i]] = k;
- }
- }
- }
- bool check(int mid, int len){
- int ma = 0, mi = MAX;
- for(int i = 1; i < len; i++){
- if(hgt[i] < mid){
- ma = 0;
- mi = MAX;
- }else{
- int minl = min(sa[i].idx, sa[i-1].idx);
- int maxr = max(sa[i].idx, sa[i-1].idx);
- mi = min(minl, mi);
- ma = max(maxr, ma);
- if(ma - mi >= mid)return true;
- }
- }
- return false;
- }
- int main(){
- freopen("in.txt", "r", stdin);
- freopen("out.txt", "w", stdout);
- int N;
- while(scanf("%d", &N) && N != 0){
- int now, pre;
- scanf("%d", &pre);
- for(int i = 0; i < N - 1; i++){
- scanf("%d", &now);
- s[i] = now - pre + 100; //计算前后数值的差,最后求其实就是长度>=4
- pre = now;
- }
- //末尾结束符
- s[N-1] = -MAX;
- build_sfx(N);
- calH(N);
- //printf("r: ");for(int i = 0; i < N; i++)printf("%d ", rank[i]);printf("/n");
- //printf("sa:");for(int i = 0; i < N; i++)printf("%d ", sa[i].idx);printf("/n");
- //printf("h: ");for(int i = 0; i < N; i++)printf("%d ", h[i]);printf("/n");
- //printf("hgt:");for(int i = 0; i < N; i++)printf("%d ", hgt[i]);printf("/n");
- int ans = -1;
- int l = 4, r = N/2+1;
- int mid;
- while(l <= r){
- mid = (l + r) / 2;
- if(check(mid, N-1)){
- l = mid + 1;
- ans = mid;
- }
- else
- r = mid - 1;
- }
- printf("%d/n", ans+1);
- }
- return 0;
- }