20200909 SCOI模拟T1(后缀数组)

T1 P5479 [BJOI2015]隐身术

思路:
发现 K ≤ 5 K≤5 K5,考虑爆搜。
考虑每个子串都是一个后缀的前缀,不妨枚举后缀
对于每个后缀(起始下标为 L L L),设 d f s ( i , j , k ) dfs(i, j, k) dfs(i,j,k) 为当前需要匹配 A i A_i Ai B j B_j Bj,还剩 k k k 次编辑的机会。
A i = B j A_i=B_j Ai=Bj,直接跳到下一个 A i + l ≠ B i + l A_i+l≠B_i+l Ai+l=Bi+l 最小的 l l l 的位置
考虑 A i ≠ B j A_i≠B_j Ai=Bj ,说明必须要进行操作匹配上 A i A_i Ai,还得保证 k > 0 k>0 k>0
考虑插入一个字符匹配 A i A_i Ai d f s ( x + 1 , y , k − 1 ) dfs(x + 1, y, k - 1) dfs(x+1,y,k1)
考虑删除 B j B_j Bj 字符 d f s ( x , y + 1 , k − 1 ) dfs(x, y + 1, k - 1) dfs(x,y+1,k1)
考虑替换 B j B_j Bj 字符变成和 A i A_i Ai 字符相同的 d f s ( x + 1 , y + 1 , k − 1 ) dfs(x + 1, y + 1, k - 1) dfs(x+1,y+1,k1)
注意去重,可以差分
对于 A i = B j A_i=B_j Ai=Bj,需要知道他们的最长公共前缀 lcp,后缀数组求
时间复杂度: O ( 3 5 n ) O(3^5n) O(35n)

代码:

#include <bits/stdc++.h>
using namespace std;
#define re register
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch()                                                                 \
  (_p1 == _p2 &&                                                             \
           (_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
       ? EOF                                                                 \
       : *_p1++)
inline int in() {
  int s = 0, f = 1;
  char x = getchar();
  for (; x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char buf[1 << 21];
int p1 = -1;
inline void flush() {
  fwrite(buf, 1, p1 + 1, stdout);
  p1 = -1;
}
inline void pc(char x) {
  if (p1 == (1 << 21) - 1) flush();
  buf[++p1] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) {
    pc('0');
    return;
  }
  if (x < 0) {
    pc('-');
    x = -x;
  }
  while (x) {
    k[++pos] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = pos; i; i--) pc(k[i]);
  return;
}
}  // namespace IO
using namespace IO;

const int A = 3e5 + 5;
const int logA = 20;
int K;
char a[A];
int n, m, len;
int sa[A], rk[A], t[A], tmp[A], p[A], hi[A], all;
int lg[A], st[A][logA];

inline void Sort() {
  for (re int i = 0; i <= all; ++i) t[i] = 0;
  for (re int i = 1; i <= len; ++i) t[rk[i]]++;
  for (re int i = 1; i <= all; ++i) t[i] += t[i - 1];
  for (re int i = len; i; --i) sa[t[rk[p[i]]]--] = p[i];
  return;
}

inline void get_sa() {
  all = 30;
  for (re int i = 1; i <= len; ++i) rk[i] = a[i] - 'A' + 1, p[i] = i;
  Sort();
  for (re int k = 1, num; k <= len; k <<= 1) {
    num = 0;
    for (re int i = len - k + 1; i <= len; ++i) p[++num] = i;
    for (re int i = 1; i <= len; ++i)
      if (sa[i] > k) p[++num] = sa[i] - k;
    Sort();
    swap(rk, tmp);
    num = 1;
    rk[sa[1]] = 1;
    for (re int i = 2; i <= len; ++i) {
      if (tmp[sa[i]] == tmp[sa[i - 1]] && tmp[sa[i] + k] == tmp[sa[i - 1] + k])
        rk[sa[i]] = num;
      else
        rk[sa[i]] = ++num;
    }
    if (num == len) break;
    all = num;
  }
  return;
}

inline void get_hi() {
  int k = 0;
  for (re int i = 1; i <= len; ++i) {
    if (rk[i] == 1) continue;
    int j = sa[rk[i] - 1];
    if (k) k--;
    while (i + k <= len && j + k <= len && a[i + k] == a[j + k]) k++;
    hi[rk[i]] = k;
  }
  return;
}

inline void prepare() {
  lg[1] = 0;
  for (re int i = 2; i <= n + m + 1; ++i) lg[i] = lg[i >> 1] + 1;
  for (re int i = 1; i <= n + m + 1; ++i) st[i][0] = hi[i];
  for (re int i = 1; i <= lg[n + m + 1]; ++i)
    for (re int j = 1; j + (1 << i) - 1 <= n + m + 1; ++j)
      st[j][i] = min(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
  return;
}

inline int lcp(int x, int y) {
  if (x > y) swap(x, y);
  x++;
  int k = lg[y - x + 1];
  return min(st[x][k], st[y - (1 << k) + 1][k]);
}

int num[A], tt;
int ans;

inline void DFS(int x, int y, int k) {
  int pp = lcp(rk[x], rk[y + n + 1]);
  x += pp, y += pp;
  if (x > n || y > m) {
    int d = k - (n - x + 1);
    if (d < 0) return;
    int l = max(1, y - d - tt), r = min(m - tt + 1, y + d - tt);
    num[l]++;
    num[r + 1]--;
  } else if (k) {
    DFS(x + 1, y, k - 1);
    DFS(x, y + 1, k - 1);
    DFS(x + 1, y + 1, k - 1);
  }
  return;
}

signed main() {
  K = in();
  scanf("%s", a + 1);
  n = strlen(a + 1);
  a[n + 1] = 'Z'+1;
  scanf("%s", a + 2 + n);
  len = strlen(a + 1);
  m = len - n - 1;
  get_sa(), get_hi();
  prepare();
  for (re int i = 1; i <= all; ++i) num[i] = 0;
  int L = max(1, n - K), R = min(m, n + K);
  for (re int i = 1; i <= m; ++i) {
    tt = i;
    DFS(1, i, K);
    for (re int j = L; j <= R; ++j) num[j] += num[j - 1];
    for (re int j = L; j <= R; ++j)
      if (num[j]) num[j] = 0, ans++;
  }
  out(ans), pc('\n');
  flush();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值