BZOJ2251: [2010Beijing Wc]外星联络
2251: [2010Beijing Wc]外星联络
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 989 Solved: 601
[Submit][Status][Discuss]
Description
小 P 在看过电影《超时空接触》(Contact)之后被深深的打动,决心致力于寻
找外星人的事业。于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星
人发来的信息。虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高
低电平将接收到的信号改写为由 0 和 1 构成的串, 并坚信外星人的信息就隐藏在
其中。他认为,外星人发来的信息一定会在他接受到的 01 串中重复出现,所以
他希望找到他接受到的 01 串中所有重复出现次数大于 1 的子串。但是他收到的
信号串实在是太长了,于是,他希望你能编一个程序来帮助他。
Input
输入文件的第一行是一个整数N ,代表小 P 接收到的信号串的长度。
输入文件第二行包含一个长度为N 的 01 串,代表小 P 接收到的信号串。
Output
输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺
序按对应的子串的字典序排列。
Sample Input
1010101
Sample Output
3
2
2
4
3
3
2
2
HINT
对于 100%的数据,满足 0 <= N <=3000
Source
【题解】
dalao们的题解:
“吼罪素组直接暴腻即可”
“裸题”
‘闲的没事了写一下证明吧’
。。。。。。。。。。。。。。。。。。。。。。。。。。。
此题把所有后缀提出来建一颗trie树还是比较好理解的一种做法,每个串的答案就是这个串末节点被走过的次数
后缀数组通过每个后缀的前缀来处理子串,所有后缀的每个前缀构成全部非空子串
于是我们尝试找与每个前缀相等的前缀
按字典序排序后,从小到大取,当前取到sa[i],只需从sa[i] + height[i]开始枚举前缀长度l,在排好序的后缀上向右拓展,直到到达末尾或者某个height[j]<l为止
为什么这样是对的呢?
首先你需要画一张图样例的height图,跟着调试程序模拟一下,你会有一点点想法,然后看下面本蒟蒻十分辣鸡的假证明从sa[i] + height[i]开始,因为sa[i] + height[i] - 1一定在之前遍历过。先看边界,height[k] = 0的时刻,能遍历到height[k + 1]这些串
设想如果height[i - 1] < height[i], 那么sa[i] + height[i-1]...sa[i] + height[i] - 1会被遍历;否则,无法遍历到,继续前推,肯定会到达height = 0的时刻,归纳证明可以遍历完。
为什么向后扩展不向前扩展或者双向扩展呢?因为长度大于跟前面的LCP,前面肯定没有呀。。。
大概这样。。大家可以拿图画画加深一下理解。。
如有谬误,还请原谅,毕竟。。。
我也不会很严谨的证明。。。
换了一个SA板子,从1开始的,这样跟AC自动机统一起来了,以后除KMP外所有字符串题都可以从1开始了。。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 #include <queue> 7 #include <vector> 8 #include <cmath> 9 #define min(a, b) ((a) < (b) ? (a) : (b)) 10 #define max(a, b) ((a) > (b) ? (a) : (b)) 11 #define abs(a) ((a) < 0 ? (-1 * (a)) : (a)) 12 13 template <class T> 14 inline void swap(T& a, T& b) 15 { 16 T tmp = a;a = b;b = tmp; 17 } 18 19 const int INF = 0x3f3f3f3f; 20 const int MAXN = 1000000 + 10; 21 22 struct SuffixArray 23 { 24 char s[MAXN]; 25 int sa[MAXN], rank[MAXN], height[MAXN]; 26 int t1[MAXN], t2[MAXN], c[MAXN]; 27 int n; 28 void build_sa(int m) 29 { 30 int i, *x = t1, *y = t2; 31 for(i = 0;i <= m;++ i) c[i] = 0; 32 for(i = 1;i <= n;++ i) ++ c[x[i] = s[i]]; 33 for(i = 1;i <= m;++ i) c[i] += c[i - 1]; 34 for(i = n;i >= 1;-- i) sa[c[x[i]] --] = i; 35 for(int k = 1;k <= n;k <<= 1) 36 { 37 int p = 0; 38 for(i = n - k + 1;i <= n;++ i) y[++ p] = i; 39 for(i = 1;i <= n;++ i) if(sa[i] > k) y[++ p] = sa[i] - k; 40 for(i = 0;i <= m;++ i) c[i] = 0; 41 for(i = 1;i <= n;++ i) ++ c[x[y[i]]]; 42 for(i = 1;i <= m;++ i) c[i] += c[i - 1]; 43 for(i = n;i >= 1;-- i) sa[c[x[y[i]]] --] = y[i]; 44 swap(x, y);p = 0,x[sa[1]] = ++ p; 45 for(i = 2;i <= n;++ i) 46 x[sa[i]] = sa[i] + k <= n && sa[i - 1] + k <= n && y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++ p; 47 if(p >= n) break;m = p; 48 } 49 } 50 void build_height() 51 { 52 int i, k = 0; 53 for(i = 1;i <= n;++ i) rank[sa[i]] = i; 54 for(i = 1;i <= n;++ i) 55 { 56 if(k) -- k; if(rank[i] == 1) continue; 57 int j = sa[rank[i] - 1]; 58 while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) ++ k; 59 height[rank[i]] = k; 60 } 61 } 62 }A; 63 64 int n; 65 66 int main() 67 { 68 scanf("%d", &n); 69 scanf("%s", A.s + 1); 70 A.n = n; 71 A.build_sa('1'); 72 A.build_height(); 73 int k; 74 for(int i = 1;i <= n;++ i) 75 for(int j = A.sa[i] + A.height[i];j <= n;++ j) 76 { 77 for(k = i;A.height[k + 1] >= j - A.sa[i] + 1;++ k); 78 if(k - i + 1 > 1) printf("%d\n", k - i + 1); 79 } 80 81 }