码迷,mamicode.com
首页 > 其他好文 > 详细

CF1063F String Journey [后缀自动机,线段树,dp]

时间:2020-05-02 18:34:32      阅读:88      评论:0      收藏:0      [点我收藏+]

标签:例子   直接   问题   false   题意   turn   lin   struct   open   

为什么题解的复杂度都带根号啊…迷惑

题意:

题目的翻译很清楚。

先把字符串翻转,容易证明答案不变。

我们考虑最优的办法,容易证明,最优解中,如果是非真子集,那么很显然长度是

(算了稍微解释一下,就是如果你多出来的就删掉,保留一个这种递增序列,这样一定最优)

\({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(n\log 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 << ‘\n‘;
	return 0;
}

CF1063F String Journey [后缀自动机,线段树,dp]

标签:例子   直接   问题   false   题意   turn   lin   struct   open   

原文地址:https://www.cnblogs.com/Isaunoya/p/12818961.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!