POJ 1743 Musical Theme

POJ_1743

    搜到的题解基本都是说去找最长的不重叠的重复的子串,但是我一直对题目描述里面的那个const带有一点纠结,比如88 88 88 1 1 1 88 88 88 1 1 1,我觉得要按const去理解的话应该输出3的,但题解都是输出的6。不过意在练后缀数组的使用,就没有太顾及这些问题了。

    这个题目关键的地方有两个:第一,重复的子串一定可以看作是某两个后缀的公共前缀,第二,把题目转化成去判定对于任意的一个长度k,是否存在长度至少为k的不重叠的重复的子串。

    转化成判定问题之后,就可以二分去解答了。在验证判定是否正确时,我们可以把相邻的所有不小于k的height[]看成一组,然后去看每个组内sa[]值的最大值与最小值之差是否满足大于等于k,如果有某一组满足这个条件,那么在这个组内就一定可以找到长度不小于k且不重叠的重复的子串。

    因为排名第i的字符串和排名第j的字符串的最长公共前缀等于height[i],height[i+1],...,height[j]中的最小值,所以把所有不小于k的height[]看成一组就保证了组内任意两个字符串的最长公共前缀都至少为k。其中最有可能形成不重叠的重复的子串就是组内sa[]值最大的字符串与sa[]值最小的字符串。

    对于后缀数组的倍增算法的代码上的一些理解,看以参考我的另外一篇文章:http://www.cnblogs.com/staginner/archive/2012/02/02/2335600.html

#include<stdio.h>
#include<string.h>
#define MAXD 20010
#define INF 0x3f3f3f3f
int N, height[MAXD], rank[MAXD], sa[MAXD], r[MAXD], wa[MAXD], wb[MAXD], ws[MAXD], wv[MAXD];
void init()
{
int i, j, k;
for(i = 0; i < N; i ++)
scanf("%d", &r[i]);
for(i = N - 1; i > 0; i --)
r[i] = r[i] - r[i - 1] + 90;
r[0] = 1, r[N] = 0;
}
int cmp(int *p, int x, int y, int l)
{
return p[x] == p[y] && p[x + l] == p[y + l];
}
void da(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(p = 1, j = 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 ++)
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, x[sa[0]] = 0, p = 1, i = 1; i < n; i ++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p ++;
}
}
void calheight(int n)
{
int i, j, k = 0;
for(i = 1; i <= n; i ++)
rank[sa[i]] = i;
for(i = 0; i < n; height[rank[i ++]] = k)
for(k ? -- k : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k ++);
}
void solve()
{
int i, j, mid, min, max, minr, maxr, ok;
da(N + 1, 200);
calheight(N);
min = 3, max = N / 2 + 1;
for(;;)
{
mid = (max + min) / 2;
if(mid == min)
break;
ok = 0, minr = INF, maxr = -1;
for(i = 1; i <= N; i ++)
{
if(height[i] < mid)
minr = maxr = sa[i];
else
{
if(sa[i] < minr)
minr = sa[i];
if(sa[i] > maxr)
maxr = sa[i];
if(maxr - minr >= mid)
{
ok = 1;
break;
}
}
}
if(ok)
min = mid;
else
max = mid;
}
if(mid > 3)
printf("%d\n", mid + 1);
else
printf("0\n");
}
int main()
{
for(;;)
{
scanf("%d", &N);
if(!N)
break;
init();
if(N < 10)
printf("0\n");
else
solve();
}
return 0;
}


你可能感兴趣的:(theme)