BZOJ 2251 Beijing WC 2010 外星联络 后缀数组

给出一个01串,求:出现次数大于1 的子串所出现的次数。
不过数据范围有点迷。。
后缀数组模板题。
构建出后缀数组以后,每个后缀都会产生子串。
比如ababa:

a
aba
ababa
ba
baba

首先第一个后缀产生了子串a,而且因为h[1],h[2]>=1,因此可以向右扩展到第三个后缀,因此出现3次。
第二个后缀产生了2个子串ab和aba,而向后h[2]>=2拓展了1次所以出现了2次。aba同理。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define rep(i,j,k) for(int i=j;i<k;i++)
#define FOR(i,j,k) for(int i=j;i<=k;i++)
const int N = 50100;
typedef long long ll;

ll read() {
    ll s = 0, f = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    for (; '0' <= ch && ch <= '9'; ch = getchar()) s = s * 10 + ch - '0';
    return s * f;
}

char str[N];
struct SuffixArray {
    char r[N];
    int n, m, num[N], z[N];
    int sa[N], rk[N], h[N];
    int wa[N], wb[N], wv[N], c[N];
    ll s[N];

    private: SuffixArray(){}
    public: SuffixArray(char *_r, int _n, int _m) {
        rep(i,0,_n) r[i]=_r[i]; r[_n] = 0;
        n = _n + 1; m = _m;
        da(); calHeight();
    }

    void *operator new(size_t) {
        static SuffixArray r;
        return &r;
    }

    void sort(int *sa, int *x, int *y, int n, int m) {
        rep(i,0,m) c[i] = 0;
        rep(i,0,n) c[x[i]]++;
        rep(i,1,m) c[i] += c[i-1];
        for(int i=n-1;i>=0;i--) sa[--c[x[i]]] = y[i];
    }

    void da() {
        int j, p, *x = wa, *y = wb, *t;
        rep(i,0,n) x[i]=r[i],z[i]=i;
        sort(sa, x, z, n, m);
        for(j=1,p=1;p<n;j*=2,m=p) {
            p=0;
            rep(i,n-j,n) y[p++] = i;
            rep(i,0,n) if(sa[i]>=j) y[p++]=sa[i]-j;
            rep(i,0,n) wv[i]=x[y[i]];
            sort(sa, wv, y, n, m);
            swap(x,y);p=1;x[sa[0]]=0;
            rep(i,1,n) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        }
    }

    void calHeight() {
        int k = 0;
        rep(i,0,n) rk[sa[i]] = i;
        rep(i,0,n-1) {
            if(k)k--;
            for(int j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
            h[rk[i]]=k;
        }
    }
};

int main() {
    int n, m, i, j, l, r;
    scanf("%d%s", &n, str);
    SuffixArray *sa = new SuffixArray(str, n, 255);
    FOR(i,1,n) for(j=sa->h[i] + 1; sa->sa[i] + j - 1 <= n; j++) {
        for (l = i; l && sa->h[l] >= j; l--);
        for (r = i + 1; r <= n && sa->h[r] >= j; r++);
        if (r - l > 1) printf("%d\n", r - l);
    }
    return 0;
}

2251: [2010Beijing Wc]外星联络

Time Limit: 30 Sec Memory Limit: 256 MB
Submit: 593 Solved: 336

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

你可能感兴趣的:(字符串,后缀数组,OI,bzoj,WinterCamp)