题目链接:http://poj.org/problem?id=1743
题目大意:给定一个序列,可从中选择一个子段,可以对这个子段各个数进行增加k或者减少k的操作,问满足子段长度大于5并且两个子段不重叠的最长的重复子段长度是多少?
解题思路:本题可用后缀数组解决。看到这题目就能想到后缀树组,因为是重复子段问题,由此观之后缀数组不仅仅解决字符串问题,其它重复子段问题也都ok,KMP也是如此。由于每个子段可进行增加k或者减少k操作,那么判断x1,y1和x2,y2能够匹配(匹配了才会重复)只要x1-y1 == x2-y2,所以将初始的长度为n的序列转化为长度为n-1的差值序列。
预处理完,便可开始求sa数组、rank数组、height数组,这些都可以套模板,自己敲也行,像我就自己敲了,但是果断错了。处理完之后,理想状态是只要找height数组中最大的值就ok了,但理想是丰满的,现实是骨感的。因为两个子段不能重叠,所以不能这样做。举个例子,22222222,9个2,如果找最大的height数组,答案是8,提交华丽丽地就Wa了。问题转化为寻找符合不重叠条件的长度大于5的最大长度,这是判定性问题,可以用二分查找从0---n/2内找最优解。每次算出mid = (maxx + minn) / 2,再将height小于mid的后缀排除,留下height大等于mid的一陀后缀,只要找其中最大的差值并判断是否大于mid即可。为什么这样判断就行呢,先想想,height小于mid的情况是它和其它各后缀的最长公共前缀长度小于mid,这不符合条件。而另外一坨height都大于mid,能否符合条件就很清楚了。
测试数据:
Input:
9
2 2 2 2 2 2 2 2 2
11
2 2 2 2 2 2 2 2 2 2 2
13
2 2 2 2 2 2 2 2 2 2 2 2 2
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
OutPut:
0
6
7
5
代码:
#include <stdio.h>
#include <string.h>
#define MAX 100000
#define max(a,b) (a) > (b) ? (a) : (b)
#define min(a,b) (a) < (b) ? (a) : (b)
int n,arr[MAX];
int wa[MAX],wb[MAX];
int wv[MAX],wn[MAX];
int sa[MAX],rank[MAX],h[MAX];
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 n,int m) {
int i,j,k,p; //p 不同字符串个数
int *x = wa,*y = wb,*t;//rank值保存在x中
//对r中长度为1的子串进行基数排序
for (i = 0; i < m; ++i) wn[i] = 0;
for (i = 0; i < n; ++i) wn[x[i]=r[i]]++;
for (i = 1; i < m; ++i) wn[i] += wn[i-1];
for (i = n - 1; i >= 0; --i) sa[--wn[x[i]]] = i;
//对r中长度为j的子串进行基数排序
for (j = 1,p = 1; p < n; j *= 2,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) wn[i] = 0;
for (i = 0; i < n; ++i) wn[wv[i]]++;
for (i = 1; i < m; ++i) wn[i] += wn[i-1];
for (i = n - 1; i >= 0; --i) sa[--wn[wv[i]]] = y[i];
t = x,x = y,y = t,p = 1;
for (x[sa[0]] = 0,i = 1; i < n; ++i)
x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p - 1: p++;
}
}
void Calheight(int *r,int n) {
int i,j,k = 0;
for (i = 1; i <= n; ++i) rank[sa[i]] = i; //sa转化为rank
for (i = 0; i < n; h[rank[i++]] = k)
for (k ? k--:0,j = sa[rank[i]-1]; r[i+k] == r[j+k]; k++);
}
int Solve(int n) {
int i,j,k,ans = 0,flag;
int minn,maxx,low,high,mid;//mid,minn和maxx指长度,low和high指sa的值
minn = 0,maxx = n / 2;
while (minn <= maxx) {
mid = minn + (maxx - minn) / 2;
flag = 0,low = high = sa[1];
for (i = 2; i <= n && !flag; ++i) {
if (h[i] < mid) low = high = sa[i];
else {
low = min(low,sa[i]);
high = max(high,sa[i]);
if (high - low >= mid) flag = 1;
}
}
if (flag) minn = mid + 1;
else maxx = mid - 1;
}
return maxx >= 4 ? maxx + 1 : 0;
}
int main()
{
int i,j,k,ans;
while (scanf("%d",&n),n) {
for (i = 0; i < n; ++i)
scanf("%d",&arr[i]);
for (i = 0; i < n - 1; ++i)
arr[i] = (arr[i] - arr[i+1]) + 100;
arr[n-1] = 0;
Da(arr,n,200);
Calheight(arr,n-1);
ans = Solve(n-1);
if (n < 10) printf("0\n");
else printf("%d\n",ans);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。