BZOJ2251: [2010Beijing Wc]外星联络

2251: [2010Beijing Wc]外星联络

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 989  Solved: 601
[Submit][Status][Discuss]

Description

小 P 在看过电影《超时空接触》(Contact)之后被深深的打动,决心致力于寻
找外星人的事业。于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星
人发来的信息。虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高
低电平将接收到的信号改写为由 0 和 1 构成的串, 并坚信外星人的信息就隐藏在
其中。他认为,外星人发来的信息一定会在他接受到的 01 串中重复出现,所以
他希望找到他接受到的 01 串中所有重复出现次数大于 1 的子串。但是他收到的
信号串实在是太长了,于是,他希望你能编一个程序来帮助他。

Input

输入文件的第一行是一个整数N ,代表小 P 接收到的信号串的长度。
输入文件第二行包含一个长度为N 的 01 串,代表小 P 接收到的信号串。

Output

输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺
序按对应的子串的字典序排列。

Sample Input

7
1010101

Sample Output

3
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 }
BZOJ2251

 

转载于:https://www.cnblogs.com/huibixiaoxing/p/8333823.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值