CodeForces 1063F. String Journey

链接:

link

题意:

定义一个字符串序列 t t t 是合法的,且仅当 t i t_i ti t i − 1 t_{i-1} ti1 的子串,并且 t i ≠ t i − 1 t_i\neq t_{i-1} ti̸=ti1

求一个最长的合法字符串序列 t t t ,满足存在一个字符串序列 u u u ,使得 s = u 1 + t 1 + u 2 + t 2 ⋯ + u k + t k + u k + 1 s = u_1 + t_1 + u_2 + t_2\cdots + u_k + t_k + u_{k+1} s=u1+t1+u2+t2+uk+tk+uk+1

题解:

为了方便,我们先将整个串翻转过来。显然存在一个最优解,使得每次将字符串长度只增加 1 1 1

d p i dp_i dpi 表示以 i i i 结尾的字符串序列最长是多长,那么显然以 i i i 结尾能凑出 d p i − 1 , d p i − 2 , ⋯   , 1 dp_i - 1, dp_i - 2, \cdots, 1 dpi1,dpi2,,1 这些长度的子序列。

注意到 d p i − 1 ≥ d p i − 1 dp_{i-1}\ge dp_i - 1 dpi1dpi1 ,所以可以直接从大到小枚举 d p i dp_i dpi 直到合法,这个步骤是 O ( n ) O(n) O(n) 的。

那么相当于检查一个前缀( i − d p i i - dp_i idpi)是否存在一个子串,它结尾处的 d p dp dp 值大于等于某个值。

注意到 i − d p i i-dp_i idpi 也是单调的,所以可以双指针,相当于在后缀自动机上单点修改,子树查询最大值。

代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 1000005;
const int M = 20;
const int ALPHA = 26;

int n, dfn, root, total, p[N], dp[N], dfnl[N], dfnr[N], value[N], parent[N], tree[N << 1], ancestor[M][N], go[N][ALPHA];
vector<int> adj[N];
char s[N];

int extend(int p, int w) {
  int np = ++total;
  value[np] = value[p] + 1;
  while (p && !go[p][w]) {
    go[p][w] = np;
    p = parent[p];
  }
  if (!p) {
    parent[np] = root;
  } else {
    int q = go[p][w];
    if (value[q] == value[p] + 1) {
      parent[np] = q;
    } else {
      int nq = ++total;
      value[nq] = value[p] + 1;
      parent[nq] = parent[q];
      parent[q] = parent[np] = nq;
      memcpy(go[nq], go[q], sizeof go[nq]);
      while (p && go[p][w] == q) {
        go[p][w] = nq;
        p = parent[p];
      }
    }
  }
  return np;
}

void dfs(int x) {
  dfnl[x] = ++dfn;
  ancestor[0][x] = parent[x];
  for (int i = 1; i < M; ++i) {
    ancestor[i][x] = ancestor[i - 1][ancestor[i - 1][x]];
  }
  for (auto y : adj[x]) {
    dfs(y);
  }
  dfnr[x] = dfn;
}

int get(int x, int length) {
  x = p[x];
  for (int i = M - 1; ~i; --i) {
    if (value[ancestor[i][x]] >= length) {
      x = ancestor[i][x];
    }
  }
  return x;
}

void modify(int x, int l, int r, int p, int value) {
  tree[x] = max(tree[x], value);
  if (l != r) {
    int y = l + r >> 1, z = x + (y - l + 1 << 1);
    if (p <= y) {
      modify(x + 1, l, y, p, value);
    } else {
      modify(z, y + 1, r, p, value);
    }
  }
}

int query(int x, int l, int r, int ql, int qr) {
  if (l == ql && r == qr) {
    return tree[x];
  } else {
    int y = l + r >> 1, z = x + (y - l + 1 << 1);
    if (qr <= y) {
      return query(x + 1, l, y, ql, qr);
    } else if (ql > y) {
      return query(z, y + 1, r, ql, qr);
    } else {
      return max(query(x + 1, l, y, ql, y), query(z, y + 1, r, y + 1, qr));
    }
  }
}

bool valid(int x) {
  if (dp[x] == 1) {
    return true;
  }
  int p = get(x, dp[x] - 1), q = get(x - 1, dp[x] - 1);
  return query(1, 1, total, dfnl[p], dfnr[p]) >= dp[x] - 1 || query(1, 1, total, dfnl[q], dfnr[q]) >= dp[x] - 1;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %s", &n, s + 1);
  reverse(s + 1, s + n + 1);
  root = total = 1;
  p[0] = root;
  for (int i = 1; i <= n; ++i) {
    p[i] = extend(p[i - 1], s[i] - 'a');
  }
  for (int i = 2; i <= total; ++i) {
    adj[parent[i]].push_back(i);
  }
  dfs(root);
  for (int i = 1, j = 0; i <= n; ++i) {
    dp[i] = dp[i - 1] + 1;
    while (!valid(i)) {
      --dp[i];
      ++j;
      modify(1, 1, total, dfnl[get(j, dp[j])], dp[j]);
    }
  }
  printf("%d\n", *max_element(dp + 1, dp + n + 1));
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值