首页 > 其他分享 >「BJOI2020」封印

「BJOI2020」封印

时间:2024-01-24 15:33:25浏览次数:24  
标签:le return SAM int max BJOI2020 封印 ch

Link:https://loj.ac/p/3298

知识点:SAM,RMQ

随手刷到的题一看是串串就做下。

这题面好随便啊呃呃

简述

给定一仅包含小写字母 \(a,b\) 的两个字符串 \(s, t\),给定 \(q\) 次询问,每次给定参数 \(l, r\),求 \(s[l:r]\) 与 \(t\) 的最长公共子串长度。
\(1\le |s|, |t|, q\le 2\times 10^5\)。
1S,512MB。

好像是套路,但我不会,学习了。

设 \(a_i\) 为前缀 \(s[1:i]\) 作为 \(t\) 的子串的最长的后缀的长度,即有:

\[a_i = \max_{1\le k\le i,\ s[i-k+1:i]\in t } \{ k \} \]

先对 \(t\) 建 SAM,则 \(a_i\) 可以通过在 SAM 上匹配 \(s\) 得到,详见:「双串最长公共子串」SP1811 LCS - Longest Common Substring。则对于询问 \([l, r]\),考虑最长公共子串是否是以 \(i(l\le i\le r)\) 结尾,则答案即为:

\[\max_{l\le i\le r}\left\{ \min\left( a_i, i - l + 1 \right) \right\} \]

发现上式中当 \(i+1\) 时,\(i-l+1\) 也加 1,而 \(a_i\) 至多加 1 或者减少。可通过子串的性质反证 \(a_i\) 不可能增加大于等于 2。则一定存在 \(p(l\le p\le r)\),满足:

\[\begin{cases} \min(a_i, i - l + 1) &= i - l + 1 &(i<p)\\ \min(a_i, i - l + 1) &= a_i &(p\le i) \end{cases}\]

即上式等于:

\[\max\left\{(p - 1) - l + 1, \max_{p\le i\le r} a_i\right\} \]

则可以对 \(a\) 维护 ST 表,每次询问先二分求得 \(p\) 后查询区间最大值即可。

总时间复杂度 \(O(|t| + |s| + m\log |s|)\) 级别。

代码

//知识点:SAM,RMQ
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, m, q;
int a[kN];
char s[kN], t[kN];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
namespace SAM {
  const int kNode = kN << 2;
  int nodenum = 1, last = 1, tr[kNode][26], len[kNode], link[kNode];
  int edgenum, head[kNode], v[kNode], ne[kNode];
  int sz[kNode];
  LL ans = 0;
  void Insert(int ch_) {
    int p = last, now = last = ++ nodenum;
    len[now] = len[p] + 1;
    sz[now] = 1;
    for (; p && !tr[p][ch_]; p = link[p]) tr[p][ch_] = now;

    if (!p) {
      link[now] = 1;
      return ;
    }

    int q = tr[p][ch_];
    if (len[q] == len[p] + 1) {
      link[now] = q;
      return ;
    }

    int newq = ++ nodenum;
    memcpy(tr[newq], tr[q], sizeof (tr[q]));
    len[newq] = len[p] + 1;
    link[newq] = link[q], link[q] = link[now] = newq;
    for (; p && tr[p][ch_] == q; p = link[p]) tr[p][ch_] = newq;
  }
  void Build(int n_, char *s_) {
    for (int i = 1; i <= n_; ++ i) Insert(s_[i] - 'a');
  }
  void Solve(int n_, char *s_) {
    int now = 1, nowlen = 0;
    for (int i = 1; i <= n_; ++ i) {
      while (now != 1 && !tr[now][s_[i] - 'a']) now = link[now], nowlen = len[now];
      if (tr[now][s[i] - 'a']) now = tr[now][s[i] - 'a'], ++ nowlen;
      a[i] = nowlen;      
    }
  }
}
namespace ST {
  int Log2[kN], f[kN][21];
  void Init() {
    Log2[1] = 0;
    for (int i = 2; i <= n; ++ i) Log2[i] = Log2[i >> 1] + 1;
    for (int i = 1; i <= n; ++ i) f[i][0] = a[i];
    for (int i = 1, l = 2; i <= 20; ++ i, l <<= 1) {
      for (int j = 1; j + l - 1 <= n; ++ j) {
        f[j][i] = std::max(f[j][i - 1], f[j + l / 2][i - 1]);
      }
    }
  }
  int Query(int l_, int r_) {
    if (l_ > r_) return 0;
    int l = Log2[r_ - l_ + 1];
    return std::max(f[l_][l], f[r_ - (1 << l) + 1][l]);
  }
}
int Query(int l_, int r_) {
  int pos = r_ + 1;
  for (int nowl = l_, nowr = r_; nowl <= nowr; ) {
    int mid = (nowl + nowr) >> 1;
    if (a[mid] < mid - l_ + 1) {
      nowr = mid - 1;
      pos = mid;
    } else {
      nowl = mid + 1;
    }
  }
  return std::max(ST::Query(pos, r_), pos - 1 - l_ + 1);
  return 0;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  scanf("%s", s + 1); n = strlen(s + 1);
  scanf("%s", t + 1); m = strlen(t + 1);
  SAM::Build(m, t);
  SAM::Solve(n, s);
  ST::Init();
  q = read();
  while (q --) {
    int l_ = read(), r_ = read();
    printf("%d\n", Query(l_ ,r_));
  }
  return 0;
}
/*
aaba
aaaabbbaa
1
2 4

aababaababbabaabbabbaabababaabbabbbabaaaaaababbaaa
bbaaabbabbaaaabbaabbaabbbabababbbaababababaaabbbbb
1
41 44
*/

标签:le,return,SAM,int,max,BJOI2020,封印,ch
From: https://www.cnblogs.com/luckyblock/p/17984800

相关文章