Jzoj P4372 识别子串___后缀数组+线段树

题目大意:

有一个长度为 L 的字符串 S ,和 S 中第 k 个字符,定义子串 T = S ( i . . j ) 为一个关于 k 的识别子串,当且仅当
1 i <= k <= j
2 T S 中只出现一次。
计算对于一个字符串S,关于S的每一位的最短识别子串分别有多长。

L = 100000

分析:

后缀数组可以求出以第 i 位开头的最短的在原串中只出现过一次的子串
子串的长度是 m i n ( h e i g h t [ r a n k [ i ] ] , h e i g h t [ r a n k [ i ] + 1 ) + 1
所以我们枚举每个位置 i ,找到这个串,然后考虑它的贡献:
对于这个串之内的位置,答案可以用这个串的长度更新;
对于这个串右边的位置,串可以向右“延伸”直到包含该位置(延伸后的串显然也只出现过一次),所以答案可以用 i + 1 来更新
然后用线段树维护即可

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>

#define fo(i, j, k) for (int i = j; i <= k; i++)
#define fd(i, j, k) for (int i = j; i >= k; i--)
#define inf 0x3f3f3f3f
#define N 500005

using namespace std;

struct Node{int rp, cp;}tree[N*4];
int height[N], rank[N*2], sa[N], s[N], b[N], c[N], d[N], cmax, cmin, n, m;
char str[N];
bool bz;

void Get_SA() {
    int i, x = 0;
    n = strlen(str + 1), m = 30;
    fo(i, 1, n) b[s[i] = str[i]-'a'+1]++;
    fo(i, 1, m) b[i] += b[i-1];
    fd(i, n, 1) c[b[s[i]]--] = i;
    fo(i, 1, n) {
        if (s[c[i]] != s[c[i-1]]) x++;
        rank[c[i]] = x;
    }
    for (int j = 1; j <= n; j <<= 1) {
        fo(i, 1, n) b[i] = 0;
        fo(i, 1, n) b[rank[i+j]]++;     
        fo(i, 1, n) b[i] += b[i-1];
        fd(i, n, 1) c[b[rank[i+j]]--] = i;
        fo(i, 1, n) b[i] = 0;
        fo(i, 1, n) b[rank[i]]++;
        fo(i, 1, n) b[i] += b[i-1];
        fd(i, n, 1) d[b[rank[c[i]]]--] = c[i];
        x = 0;
        fo(i, 1, n) {
            if (rank[d[i]] != rank[d[i-1]] || rank[d[i]] == rank[d[i-1]] && rank[d[i]+j] != rank[d[i-1]+j]) x++;
            c[d[i]] = x;
        }
        fo(i, 1, n) rank[i] = c[i];
        if (x == n) break;
    }
}
void Get_height() {
    int i, x = 0;
    fo(i, 1, n) sa[rank[i]] = i;
    fo(i, 1, n) {
        if (x) x--;
        int j = sa[rank[i]-1];
        while (i+x <= n && j+x <= n && s[i+x] == s[j+x]) x++;
        height[rank[i]] = x;
    }
    height[1] = 0;
}

void build(int x, int l, int r) {
    tree[x].cp = -inf; tree[x].rp = inf;
    if (l == r) return;
    int mid = (l+r)>>1;
    build(x*2, l, mid), build(x*2+1, mid+1, r);
}

void Add(int x, int l, int r, int x1, int x2, int num) {
    if (x1 == l && x2 == r) { 
        if (bz) tree[x].rp = min(tree[x].rp, num); 
           else tree[x].cp = max(tree[x].cp, num);
        return; 
    }
    int mid = (l+r)>>1;
    if (x2 <= mid) Add(x*2, l, mid, x1, x2, num);
              else if (x1 > mid) Add(x*2+1, mid+1, r, x1, x2, num);
                            else Add(x*2, l, mid, x1, mid, num), Add(x*2+1, mid+1, r, mid+1, x2, num);
}

void query(int x, int l, int r, int num) {
    cmin = min(cmin, tree[x].rp); 
    cmax = max(cmax, tree[x].cp);
    if (l == r) return;
    int mid = (l+r)>>1;
    if (num <= mid) query(x*2, l, mid, num);
               else query(x*2+1, mid+1, r, num);
}

int main() {
    int i;
    scanf("%s", str+1); 
    Get_SA(); 
    Get_height();
    build(1, 1, n);
    fo(i, 1, n) {
        int x = sa[i], len = max(height[i], height[i+1]);
        if (len == n-x+1) continue;
        bz = 1; Add(1, 1, n, x, x+len, len+1);
        if (x+len+1 <= n) bz = 0, Add(1, 1, n, x+len+1, n, x);
    }
    fo(i, 1, n) {
        cmin = inf, cmax = -inf; 
        query(1, 1, n, i);
        printf("%d\n", min(cmin, i-cmax+1));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/gx_man_vip/article/details/81044022
今日推荐