对应POJ题目:点击打开链接
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 19985 | Accepted: 6835 |
Description
Input
Output
Sample Input
30 25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18 82 78 74 70 66 67 64 60 65 80 0
Sample Output
5
Hint
题意:给出n个范围在1~88的音符,求最长的重复主题的长度
重复主题:1,不可重叠; 2,某子串所有元素加减某个数可得到另一个子串;
如:1 4 3 6 24 5 6 34 7 6 9 中, 4 3 6 和 7 6 9 就是重复的主题,因为 4 3 6 中每个元素加上3 就跟 跟它主题重复的子串相同。
思路:令所有 val[i] = val[i+1] - val[i],则不需要进行转换,然后每个元素都加上88,再在末尾添加个元素0,之后就是范围在0~175的后缀数组求不可重叠的最长重复子串。最终结果+1.
留意一下这个例子:
11
1 2 3 4 5 6 7 8 9 10 11
答案应该是5, 我相信比较多人会输出6
虽然POJ无视此问题,但我还是在输出时做了一点小判断。。。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MS(x, y) memset(x, y, sizeof(x)) const int MAXN = 20000+10; const int INF = 1<<30; int wa[MAXN],wb[MAXN],wv[MAXN],ws[MAXN]; int rank[MAXN],r[MAXN],sa[MAXN],height[MAXN]; int num[MAXN]; 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 *sa, int n, int m) { int i, j, p, *x = wa, *y = wb, *t; for(i=0; i<m; i++) ws[i] = 0; for(i=0; i<n; i++) ws[x[i] = r[i]]++; for(i=1; i<m; i++) ws[i] += ws[i-1]; for(i=n-1; i>=0; i--) sa[--ws[x[i]]] = i; for(j=1,p=1; p<n; j<<=1, 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++) ws[i] = 0; for(i=0; i<n; i++) ws[wv[i]]++; for(i=1; i<m; i++) ws[i] += ws[i-1]; for(i=n-1; i>=0; i--) sa[--ws[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++; } return; } void calheight(int *r, int *sa, int n) { int i, j, k = 0; for(i=1; i<n; i++) rank[sa[i]] = i; for(i=0; i<n-1; height[rank[i++]] = k) for(k ? k-- : 0,j=sa[rank[i]-1]; r[i+k] == r[j+k]; k++); return; } int main() { //freopen("in.txt", "r", stdin); int n; while(~scanf("%d", &n), n) { int i; for(i=0; i<n; i++) scanf("%d",r + i); if(n < 10){ printf("0\n"); continue; } for(i=0; i<n-1; i++) r[i] = r[i+1] - r[i]; for(i=0; i<n-1; i++) r[i] += 88; r[n-1] = 0; da(r, sa, n, 200); calheight(r, sa, n); int left = 1, right = n; int ok; int ans = 0; int beg, end; while(left <= right) { int mid = left + (right - left)/2; //printf("%d\n", mid); beg = end = ok = 0; for(int i=2; i<n; i++){ if(height[i] >= mid){//确定某一组的起点终点 if(!beg) beg = i; end = i; } if((beg && end) && (i == n - 1 || height[i] < mid)){ int minn = INF, maxn = -1; for(int i=beg-1; i<=end; i++){ if(sa[i] > maxn) maxn = sa[i]; if(sa[i] < minn) minn = sa[i]; } if(maxn - minn >= mid){//子串不重叠 ok = 1; break; } beg = end = 0; } } if(ok) ans = mid; if(ok) left = mid + 1; else right = mid - 1; } if(ans < 4) printf("0\n"); else{ ans += 1; if(ans-1 == n/2 && n%2) ans -= 1; printf("%d\n", ans); } } return 0; }