CF1063F String Journey [后缀自动机,线段树,dp]
Posted isaunoya
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1063F String Journey [后缀自动机,线段树,dp]相关的知识,希望对你有一定的参考价值。
为什么题解的复杂度都带根号啊…迷惑
题意:
题目的翻译很清楚。
先把字符串翻转,容易证明答案不变。
我们考虑最优的办法,容易证明,最优解中,如果是非真子集,那么很显然长度是
(算了稍微解释一下,就是如果你多出来的就删掉,保留一个这种递增序列,这样一定最优)
({1,2,3,4,5,6…})
然后的话考虑怎么判定,假设当前的 (dp_i = x)。
那么一定存在一个长度为 (x) 的序列,但是我们并不知道它是长度为 (x-1) 的字符串添加一个字符到前面还是添加到后面
(举个例子,就是你并不知道 a 后面那个字符串是 ba 还是 ab)
然后变成一个判定性问题,只需要满足 (s_{1,i-f_i}) 存在一个 (s_{j-f_j+1,j}) 满足该串的结尾 (f_j geq f_i-1) 。然后我们注意到,(i-f_i) 是单调不降的所以我们直接指针扫一下就好了,搞个 SAM + 子树最值。
复杂度 (O(nlog n))
// by Isaunoya
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 61;
struct sam {
int ch[maxn][26], fa[maxn], len[maxn];
int las, cnt;
sam() { las = cnt = 1; }
void ins(int c) {
int p = las, np = las = ++ cnt;
len[np] = len[p] + 1;
for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) { fa[np] = 1; }
else {
int q = ch[p][c];
if(len[q] == len[p] + 1) { fa[np] = q; }
else {
int nq = ++ cnt;
memcpy(ch[nq], ch[q], sizeof(ch[q])), len[nq] = len[p] + 1;
fa[nq] = fa[q], fa[q] = fa[np] = nq;
for(; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
}
} sam;
int rt[maxn];
struct smt {
int mx[maxn << 2];
void chg(int l, int r, int p, int x, int v) {
if(l == r) { mx[p] = max(mx[p], v); return; }
int mid = l + r >> 1;
if(x <= mid) chg(l, mid, p << 1, x, v);
else chg(mid + 1, r, p << 1 | 1, x, v);
mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
}
int qry(int a, int b, int l, int r, int p) {
if(a <= l && r <= b) { return mx[p]; }
int mid = l + r >> 1, ans = 0;
if(a <= mid) ans = max(ans, qry(a, b, l, mid, p << 1));
if(b > mid) ans = max(ans, qry(a, b, mid + 1, r, p << 1 | 1));
return ans;
}
} smt;
int n;
char s[maxn];
vector <int> g[maxn];
int pos[maxn], f[maxn][22];
int kth(int x, int k) {
for(int i = 20; ~i; i --)
if(f[x][i] && sam.len[f[x][i]] >= k) x = f[x][i];
return x;
}
int dfn[maxn], dfo[maxn], idx = 0;
void dfs(int u) {
dfn[u] = ++ idx;
for(int v: g[u]) { f[v][0] = u; dfs(v); }
dfo[u] = idx;
}
int dp[maxn];
int chk(int id) {
int x = kth(pos[id], dp[id] - 1);
int y = kth(pos[id - 1], dp[id] - 1);
if(smt.qry(dfn[x], dfo[x], 1, idx, 1) >= dp[id] - 1)
return 1;
if(smt.qry(dfn[y], dfo[y], 1, idx, 1) >= dp[id] - 1)
return 1;
return 0;
}
signed main() {
#ifdef LOCAL
freopen("testdata.in", "r", stdin);
#endif
ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for(int i = 1 ; i <= n ; i ++) cin >> s[i];
reverse(s + 1, s + n + 1);
for(int i = 1 ; i <= n ; i ++) sam.ins(s[i] - ‘a‘), pos[i] = sam.las;
for(int i = 2 ; i <= sam.cnt ; i ++) g[sam.fa[i]].push_back(i);
dfs(1); for(int j = 1 ; j <= 20 ; j ++) for(int i = 1 ; i <= idx ; i ++) f[i][j] = f[f[i][j - 1]][j - 1];
for(int i = 1, j = 0; i <= n ; i ++) {
dp[i] = dp[i - 1] + 1;
while(!chk(i)) {
--dp[i], ++j;
smt.chg(1, idx, 1, dfn[pos[j]], dp[j]);
}
}
int ans = 0;
for(int i = 1 ; i <= n ; i ++) ans = max(ans, dp[i]);
cout << ans << ‘
‘;
return 0;
}
以上是关于CF1063F String Journey [后缀自动机,线段树,dp]的主要内容,如果未能解决你的问题,请参考以下文章
CF1063F String Journey [后缀自动机,线段树,dp]