zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    C#_基础_继承以及关键字new(二十一)
    C#_基础_静态关键字static(二十一)
    C#_基础_密封类和密封方法关键字sealed(二十)
    C#_基础_多线程(十九)
    C#_基础_部分类partial(十八)
    C#_基础_文件夹操作类Directory(十七)
    C#_基础_简单实现自定义异常(十六)
    C#_基础_String和StringBuilder及字符串常用方法(十五)
    C#基础_递归方法几个示例(十四)
    C#_基础_关键字可变参数Param(十三)
  • 原文地址:https://www.cnblogs.com/Isaunoya/p/12818961.html
Copyright © 2011-2022 走看看