【bzoj2251】[2010Beijing Wc]外星联络 后缀数组
题目描述
小 P 在看过电影《超时空接触》(Contact)之后被深深的打动,决心致力于寻找外星人的事业。于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星人发来的信息。虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高低电平将接收到的信号改写为由 0 和 1 构成的串, 并坚信外星人的信息就隐藏在其中。他认为,外星人发来的信息一定会在他接受到的 01 串中重复出现,所以他希望找到他接受到的 01 串中所有重复出现次数大于 1 的子串。但是他收到的信号串实在是太长了,于是,他希望你能编一个程序来帮助他。
输入
输入文件的第一行是一个整数N ,代表小 P 接收到的信号串的长度。
输入文件第二行包含一个长度为N 的 01 串,代表小 P 接收到的信号串。
输出
输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺
序按对应的子串的字典序排列。
样例输入
7
1010101
样例输出
3
3
2
2
4
3
3
2
2
题解
后缀数组
先求出sa和height数组,然后枚举。
由于字符串有一个性质:字符串的所有字串就是所有后缀的所有前缀。
那么我们可以枚举每个字符向后延续的长度。
然后向右循环,看有多少个height大于该长度。
注意:(1)枚举长度时要从height+1开始,因为前面的都已经处理过。
(2)循环时不能向左循环,因为左边的已经找过。
(既找l又找r的题解是逗我吗?j从height[i]+1开始,l从i开始,连初始条件height[l]>=j都不满足,直接跳出循环)
最后判断一下出现次数是否大于1即可。
#include <cstdio> #include <cstring> #define N 3001 using namespace std; int ws[N] , wv[N] , wa[N] , wb[N] , sa[N] , r[N] , n; int rank[N] , height[N]; char str[N]; void da() { int i , j , p , *x = wa , *y = wb , *t , m = 3; 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 >= 0) 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 = i = 1 ; i < n ; i ++ ) { if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) x[sa[i]] = p - 1; else x[sa[i]] = p ++ ; } } for(i = 1 ; i < n ; i ++ ) rank[sa[i]] = i; for(i = p = 0 ; i < n - 1 ; height[rank[i ++ ]] = p) for(p ? p -- : 0 , j = sa[rank[i] - 1] ; r[i + p] == r[j + p] ; p ++ ); } int main() { int i , j , k , len; scanf("%d%s" , &len , str); for(i = 0 ; i < len ; i ++ ) r[i] = str[i] - '0' + 1; r[len] = 0; n = len + 1; da(); for(i = 1 ; i <= len ; i ++ ) { for(j = height[i] + 1 ; sa[i] + j - 1 < len ; j ++ ) { for(k = i + 1 ; height[k] >= j && k <= len ; k ++ ); if(k - i > 1) printf("%d\n" , k - i); } } return 0; }