【BZOJ-2251】外星联络 后缀数组 + 暴力
2251: [2010Beijing Wc]外星联络
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 670 Solved: 392
[Submit][Status][Discuss]
Description
小 P 在看过电影《超时空接触》(Contact)之后被深深的打动,决心致力于寻找外星人的事业。于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星
人发来的信息。虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高低电平将接收到的信号改写为由 0 和 1 构成的串, 并坚信外星人的信息就隐藏在
其中。他认为,外星人发来的信息一定会在他接受到的 01 串中重复出现,所以他希望找到他接受到的 01 串中所有重复出现次数大于 1 的子串。但是他收到的信号串实在是太长了,于是,他希望你能编一个程序来帮助他。
Input
输入文件的第一行是一个整数N ,代表小 P 接收到的信号串的长度。
输入文件第二行包含一个长度为N 的 01 串,代表小 P 接收到的信号串。
Output
输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺
序按对应的子串的字典序排列。
Sample Input
7
1010101
1010101
Sample Output
3
3
2
2
4
3
3
2
2
3
2
2
4
3
3
2
2
HINT
对于 100%的数据,满足 0 <= N <=3000
Source
Solution
后缀数组..套路求完SA,height后暴力一下
求一个字串出现的次数,只要从该字串第一次出现的时候 暴力往后匹配即可
所以枚举排名为i的起点,从height[i]+1开始算就好了...至于为什么要+1,如果直接从height开始算.SA[i]和SA[i-1]开头的串会重复计算,所以要+1
Code
#include<cstdio> #include<cstring> #include<cmath> using namespace std; #define maxn 3010 char S[maxn];int SA[maxn],N; int wa[maxn],wb[maxn],ws[maxn],wv[maxn]; int cmp(int *r,int a,int b,int l) { return r[a]==r[b]&&r[a+l]==r[b+l]; } void DA(char *r,int *sa,int n,int m) { int p,*x=wa,*y=wb,*t; for (int i=0; i<m; i++) ws[i]=0; for (int i=0; i<n; i++) ws[x[i]=r[i]]++; for (int i=1; i<m; i++) ws[i]+=ws[i-1]; for (int i=n-1; i>=0; i--) sa[--ws[x[i]]]=i; p=1; for (int j=1; p<n; j*=2,m=p) { p=0; for (int i=n-j; i<n; i++) y[p++]=i; for (int i=0; i<n; i++) if (sa[i]>=j) y[p++]=sa[i]-j; for (int i=0; i<n; i++) wv[i]=x[y[i]]; for (int i=0; i<m; i++) ws[i]=0; for (int i=0; i<n; i++) ws[wv[i]]++; for (int i=1; i<m; i++) ws[i]+=ws[i-1]; for (int i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i]; t=x,x=y,y=t;p=1;x[sa[0]]=0; for (int i=1; i<n; i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } } int rank[maxn],height[maxn]; void calheight(char *r,int *sa,int n) { int k=0; for (int i=1; i<=n; i++) rank[sa[i]]=i; for (int i=0; i<n; height[rank[i++]]=k) {k?k--:0;for (int j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);} } int main() { scanf("%d",&N); scanf("%s",S); for (int i=0; i<N; i++) S[i]=S[i]-'0'+1; S[N]=0; DA(S,SA,N+1,3); calheight(S,SA,N); for (int i=1; i<=N; i++) for (int j=height[i]+1; SA[i]+j-1<=N; j++) { int l,r; for (l=i; height[l]>=j&&l>=1; l--); for (r=i+1; height[r]>=j&&r<=N; r++); if (r-l>1) printf("%d\n",r-l); } return 0; }
悲伤的故事..手误打错一个字符...WA成狗还一直没看出来....最后气急败坏重新敲了一遍1A了...
——It's a lonely path. Don't make it any lonelier than it has to be.