CF700E Cool Slogans

题意

给出一个长度为 \(n\) 的字符串 \(s[1]\),由小写字母组成。定义一个字符串序列 \(s[1....k]\) ,满足性质:\(s[i]\)\(s[i-1]\) \((i \ge 2)\) 中出现至少两次(位置可重叠),问最大的 \(k\) 是多少,使得从 \(s[1]\) 开始到 \(s[k]\) 都满足这样一个性质。

Sol

某模拟赛出了这样一道题
然后因为测试没开无限栈,没有AC

建立后缀自动机,那么每个点就是字符串中的一个子串
\(f[i]\) 表示以 \(i\) 这个子串结尾的最大的答案
显然在 \(parent\) 树上,\(f[i]\) 可以由父亲转移而来
然后考虑可以接在什么样的串后面
如果 \(i\) 可以接在 \(j\) 后面,那么 \(i\) 也可以接在 \(j\) 的某个前缀(包括自己)的后面,并且这个前缀的某个后缀就是 \(i\)
这其实就是 \(parent\) 树上的祖先和后代的关系

怎么样找到这样一个点?
假设 \(i\) 要找到一个 \(j\)
显然 \(j\)\(right\) 集合包含 \(i\) 的集合,不论 \(i\) 出现在 \(right\) 集合的那个位置上,它始终含有 \(j\) 这个子串,并且有 \(\ge2\)
所以 \(j\) 只要满足 \(i\) 的任意一个位置 \(pos\)
使得 \(j\)\(right\) 集合有 \([pos-len_i+len_j,pos-1]\) 区间内的元素,并且 \(j\)\(i\) 的祖先即可
\(right\) 集合和查询直接线段树合并+查询即可

暴力找显然不划算
转移都出来了,那么 \(f\) 显然是从祖先到后代不降的
显然如果 \(j\) 满足 \(i\) 的要求,那么 \(j\) 的祖先显然也满足,所以可以倍增找
复杂度 \(O(nlog_n^2n)\)

或者是直接在一条链上 \(two-points\) 即可
复杂度 \(O(nlog_nn)\)

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
# define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
using namespace std;
typedef long long ll;

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

const int maxn(4e5 + 5);

int n, tot, last, trans[26][maxn], fa[maxn], len[maxn];
int first[maxn], nxt[maxn], rt[maxn], sz[maxn * 20], cnt, ls[maxn * 20], rs[maxn * 20];
int nt[maxn], fp[maxn], f[maxn], pos[maxn], ans;
char s[maxn];

IL void Modify(RG int &x, RG int l, RG int r, RG int p){
    if(!x) x = ++cnt;
    ++sz[x];
    if(l == r) return;
    RG int mid = (l + r) >> 1;
    p <= mid ? Modify(ls[x], l, mid, p) : Modify(rs[x], mid + 1, r, p);
}

IL int Merge(RG int x, RG int y){
    if(!x || !y) return x + y;
    RG int z = ++cnt;
    sz[z] = sz[x] + sz[y];
    ls[z] = Merge(ls[x], ls[y]);
    rs[z] = Merge(rs[x], rs[y]);
    return z;
}

IL int Query(RG int x, RG int l, RG int r, RG int ql, RG int qr){
    if(!x || l > r) return 0;
    if(ql <= l && qr >= r) return sz[x];
    RG int mid = (l + r) >> 1, sum = 0;
    if(ql <= mid) sum = Query(ls[x], l, mid, ql, qr);
    if(qr > mid) sum += Query(rs[x], mid + 1, r, ql, qr);
    return sum;
}

IL void Extend(RG int c, RG int id){
    RG int p = last, np = ++tot;
    len[last = np] = len[p] + 1, pos[np] = id;
    while(p && !trans[c][p]) trans[c][p] = np, p = fa[p];
    if(!p) fa[np] = 1;
    else{
        RG int q = trans[c][p];
        if(len[q] == len[p] + 1) fa[np] = q;
        else{
            RG int nq = ++tot;
            fa[nq] = fa[q], len[nq] = len[p] + 1;
            for(RG int i = 0; i < 26; ++i) trans[i][nq] = trans[i][q];
            fa[np] = fa[q] = nq;
            while(p && trans[c][p] == q) trans[c][p] = nq, p = fa[p];
        }
    }
    Modify(rt[last], 1, n, id);
}

IL void Dfs1(RG int u){
    for(RG int v = first[u]; v; v = nxt[v]){
        Dfs1(v);
        rt[u] = Merge(rt[u], rt[v]);
        if(!pos[u]) pos[u] = pos[v];
    }
}

IL void Dfs2(RG int u){
    fp[u] = 1;
    if(u != 1){
        if(fa[u] == 1) f[u] = 1;
        else{
            RG int p = fp[fa[u]]; f[u] = f[fa[u]];
            while(p != u && Query(rt[p], 1, n, pos[u] - len[u] + len[p], pos[u] - 1)){
                f[u] = max(f[u], f[fp[u] = p] + 1);
                p = nt[p];
            }
        }
    }
    for(RG int v = first[u]; v; v = nxt[v]) nt[u] = v, Dfs2(v);
}

int main(){
    n = Input(), scanf(" %s", s + 1), tot = last = 1;
    for(RG int i = 1; i <= n; ++i) Extend(s[i] - 'a', i);
    for(RG int i = 1; i <= tot; ++i) nxt[i] = first[fa[i]], first[fa[i]] = i;
    Dfs1(1), Dfs2(1);
    for(RG int i = 1; i <= tot; ++i) ans = max(ans, f[i]);
    printf("%d\n", ans);
    return 0;
}
posted @ 2018-06-12 15:37 Cyhlnj 阅读(...) 评论(...) 编辑 收藏