zoukankan      html  css  js  c++  java
  • CF955D Scissors

    CF955D Scissors

    题目来源:Codeforces, Codeforces Round #471 (Div. 2), CF#471, CF955D Scissors

    题目大意

    题目链接

    给定一个长度为 (n) 的串 (s) 和一个长度为 (m) 的串 (t)。给定一个正整数 (k)

    请找出 (s) 的两个互不重叠的、长度为 (k) 的子串,满足:将它们按原有顺序拼接后,得到的串中包含 (t)(t) 是一个子串)。

    数据范围:(2leq mleq 2cdot kleq nleq 5 imes 10^5)

    本题题解

    考虑 (s) 里,被选出的两个长度为 (k) 的子串,称为关键子串 1、关键子串 2。

    分两种情况:

    1. (t) 被完整地包含在某个关键子串中;
    2. (t) 在两个关键子串的拼接处。

    对于情况 1,做一遍普通的 KMP 即可判断。因此以下只讨论情况 2。

    枚举关键子串 1 的结尾位置 (i),用 KMP 可以求出一个最大的 (f_i),满足 (s[i - f_i + 1, i] = t[1,f_i])。同理,枚举关键子串 2 的开头位置 (j),可以求出一个最大的 (g_j),满足 (s[j,j + g_j - 1] = t[m - g_j + 1, m])

    如果存在一对 (kleq i < jleq n - k + 1),满足 (f_i + g_j = m),那么我们已经找出了答案。

    然而,如果没找到这样的 (i,j),并不一定代表无解。事实上,上述做法的误区是,数值大的 (f_i), (g_j),并不一定最优。具体来说,当 (kleq i < 2k) 时,可能存在 (s[i - f_i +i]) 的一个 border,由它作为 (t) 的前缀,去和后面拼接,能得到答案。对于 (g) 也是类似的。

    如果枚举 (i),再暴力枚举 border,由于 border 的数量最大有 (mathcal{O}( ext{串长})) 个(例如串 ( exttt{aaa...a})),这样时间复杂度最坏为 (mathcal{O}(km)),无法通过本题。

    考虑优化这个“暴力跳 border”的过程。设 (t[1,x]) 的 border 长度为 ( ext{fail}(x))(与 KMP 算法里的定义是一样的),对于所有 (1leq xleq m) 如果 ( ext{fail}(x) eq 0),我们从 ( ext{fail}(x))(x) 连一条边,发现可以得到一个有根树森林。“暴力跳 border”,就相当于在枚举一个节点 (x) 的所有祖先。因此可以用树链剖分优化。更具体地,由于一前一后各做一次 KMP,实际上需要建出两个森林。

    考虑从小到大枚举 (j),每次 (i = j - 1) 会成为一个新出现的、可能的 (i),我们把 (f_i) 在树上的所有祖先打上标记。然后要对 (g_j) 的所有祖先进行查询。通过树链剖分和 dfs 序,把树上问题转化为序列问题后,问题可以形式化地描述为:

    有两个排列 (p_{1dots m}, q_{1dots m})(也就是两棵树的 dfs 序序列),需要支持若干次操作。操作分为如下两种:

    1. 给定区间 ([l,r]),把 (p_{ldots r}) 里所有数值打上标记。
    2. 给定区间 ([l,r]),查询 (q_{ldots r}) 里是否存在被标记过的数值。

    这个问题并不难。考虑离线,预处理出每个数值第一次被标记的时间,这可以通过倒序遍历操作,转化为区间覆盖问题。然后查询就变成了区间最小值查询,可以用 ST 表实现。

    如果用线段树做区间覆盖,因为外层还要跳 (mathcal{O}(log n)) 条重链,总时间复杂度是 (mathcal{O}(nlog^2 n))(n,m,k) 同阶)。可以通过本题,但不够优秀。进一步观察发现,区间覆盖操作是静态的(所有修改发生在询问之前),因此可以用并查集维护。总时间复杂度优化为 (mathcal{O}(nlog n))

    参考代码

    建议使用快速输入、输出,详见本博客公告。

    // problem: CF955D
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    
    template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
    template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
    
    const int MAXN = 5e5;
    const int LOG = 18;
    const int INF = 1e9;
    
    class KMP {
    public:
    	int fail[MAXN + 5], match[MAXN + 5];
    	void get_fail(const char* t, int m) {
    		fail[1] = 0;
    		for (int i = 2, j = 0; i <= m; ++i) {
    			while (j && t[i] != t[j + 1])
    				j = fail[j];
    			if (t[i] == t[j + 1])
    				++j;
    			
    			fail[i] = j;
    			// cerr << j << " 
    "[i == m];
    		}
    	}
    	void get_match(const char* s, const char* t, int n, int lim) {
    		for (int i = 1, j = 0; i <= n; ++i) {
    			while (j && (s[i] != t[j + 1] || j >= lim))
    				j = fail[j];
    			if (s[i] == t[j + 1])
    				++j;
    			
    			match[i] = j;
    			// cerr << j << " 
    "[i == n];
    		}
    	}
    	KMP() {}
    };
    
    class Tree {
    public:
    	struct EDGE { int nxt, to; } edge[MAXN * 2 + 5];
    	int head[MAXN + 5], tot;
    	inline void add_edge(int u, int v) { edge[++tot].nxt = head[u]; edge[tot].to = v; head[u] = tot; }
    	
    	int fa[MAXN + 5], sz[MAXN + 5], son[MAXN + 5], dep[MAXN + 5];
    	void dfs1(int u) {
    		sz[u] = 1;
    		for (int i = head[u]; i; i = edge[i].nxt) {
    			int v = edge[i].to;
    			if (v == fa[u])
    				continue;
    			fa[v] = u;
    			dep[v] = dep[u] + 1;
    			dfs1(v);
    			sz[u] += sz[v];
    			if (!son[u] || sz[v] > sz[son[u]])
    				son[u] = v;
    		}
    	}
    	int top[MAXN + 5], dfn[MAXN + 5], ofn[MAXN + 5], rev[MAXN + 5], cnt_dfn;
    	void dfs2(int u, int t) {
    		top[u] = t;
    		dfn[u] = ++cnt_dfn;
    		rev[cnt_dfn] = u;
    		if (son[u])
    			dfs2(son[u], t);
    		for (int i = head[u]; i; i = edge[i].nxt) {
    			int v = edge[i].to;
    			if (v == fa[u] || v == son[u])
    				continue;
    			dfs2(v, v);
    		}
    		ofn[u] = cnt_dfn;
    	}
    	bool is_anc(int anc, int v) {
    		return dfn[anc] <= dfn[v] && ofn[anc] >= dfn[v];
    	}
    	Tree() {}
    };
    
    class StaticRangeCover {
    private:
    	int len;
    	int fa[MAXN + 5], sz[MAXN + 5], mx[MAXN + 5], res[MAXN + 5];
    	vector<pair<pii, int> > vec;
    	int get_fa(int u) {
    		return (u == fa[u]) ? u : (fa[u] = get_fa(fa[u]));
    	}
    	void unite(int u, int v) {
    		u = get_fa(u);
    		v = get_fa(v);
    		if (u != v) {
    			if (sz[u] > sz[v])
    				swap(u, v);
    			fa[u] = v;
    			sz[v] += sz[u];
    			mx[v] = max(mx[v], mx[u]);
    		}
    	}
    	void jump_and_cover(int l, int r, int val) {
    		for (int i = l; i <= r; ++i) {
    			i = mx[get_fa(i)];
    			if (i > r) {
    				break;
    			}
    			res[i] = val;
    			unite(i, r + 1);
    		}
    	}
    public:
    	void build(int _len) {
    		len = _len;
    		for (int i = 1; i <= len + 1; ++i) {
    			fa[i] = i;
    			sz[i] = 1;
    			mx[i] = i;
    			res[i] = INF;
    		}
    	}
    	void range_cover(int l, int r, int v) {
    		vec.pb(mk(mk(l, r), v));
    	}
    	void get_res(int* _res) {
    		for (int i = SZ(vec) - 1; i >= 0; --i) {
    			jump_and_cover(vec[i].fi.fi, vec[i].fi.se, vec[i].se);
    		}
    		for (int i = 1; i <= len; ++i) {
    			_res[i] = res[i];
    		}
    	}
    	StaticRangeCover() {}
    };
    
    class RangeMinQuery {
    private:
    	int _log2[MAXN + 5];
    	pii st[MAXN + 5][LOG + 1];
    public:
    	void build(int* a, int n) {
    		_log2[0] = -1;
    		for (int i = 1; i <= n; ++i) {
    			_log2[i] = _log2[i >> 1] + 1;
    			st[i][0] = mk(a[i], i);
    		}
    		for (int j = 1; j <= LOG; ++j) {
    			for (int i = 1; i + (1 << (j - 1)) <= n; ++i) {
    				st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
    			}
    		}
    	}
    	pii rmq(int l, int r) {
    		int k = _log2[r - l + 1];
    		return min(st[l][k], st[r - (1 << k) + 1][k]);
    	}
    	int rmq_val(int l, int r) { return rmq(l, r).fi; }
    	int rmq_pos(int l, int r) { return rmq(l, r).se; }
    	RangeMinQuery() {}
    };
    
    int n, m, K;
    char s[MAXN + 5], t[MAXN + 5];
    
    KMP kmp, kmp_rev;
    Tree tree, tree_rev;
    
    StaticRangeCover DSU;
    RangeMinQuery RMQ;
    
    void cover_anc(int u, int tim) {
    	while (u) {
    		int l = tree.dfn[tree.top[u]], r = tree.dfn[u];
    		DSU.range_cover(l, r, tim);
    		u = tree.fa[tree.top[u]];
    	}
    }
    
    int first_cov_tim[MAXN + 5];
    int arr[MAXN + 5];
    void query_anc(int u, int tim) {
    	// u 的祖先, 有没有人在第 tim 时刻之前(严格小于)被覆盖了?
    	while (u) {
    		int l = tree_rev.dfn[tree_rev.top[u]], r = tree_rev.dfn[u];
    		if (RMQ.rmq_val(l, r) < tim) {
    			cout << "Yes" << endl;
    			int x = tree_rev.rev[RMQ.rmq_pos(l, r)] - 1;
    			for (int i = K; i < tim; ++i) {
    				int v = kmp.match[i];
    				if (v != 0 && tree.is_anc(x, v)) {
    					cout << i - K + 1 << " " << tim << endl;
    					exit(0);
    				}
    			}
    			assert(0);
    		}
    		u = tree_rev.fa[tree_rev.top[u]];
    	}
    }
    int main() {
    	cin >> n >> m >> K;
    	cin >> (s + 1);
    	cin >> (t + 1);
    	
    	kmp.get_fail(t, m);
    	kmp.get_match(s, t, n, min(m, K));
    	reverse(s + 1, s + n + 1);
    	reverse(t + 1, t + m + 1);
    	kmp_rev.get_fail(t, m);
    	kmp_rev.get_match(s, t, n, min(m, K));
    	
    	if (m <= K) {
    		for (int i = m; i <= n - K; ++i) {
    			if (kmp.match[i] == m) {
    				cout << "Yes" << endl;
    				cout << max(1, i - K + 1) << " " << n - K + 1 << endl;
    				return 0;
    			}
    		}
    		for (int i = K + 1; i <= n - m + 1; ++i) {
    			if (kmp_rev.match[n - i + 1] == m) {
    				cout << "Yes" << endl;
    				cout << 1 << " " << min(i, n - K + 1) << endl;
    				return 0;
    			}
    		}
    	}
    	
    	for (int i = 2; i <= m; ++i) {
    		if (kmp.fail[i]) {
    			tree.add_edge(kmp.fail[i], i);
    		}
    		if (kmp_rev.fail[i]) {
    			tree_rev.add_edge(m - kmp_rev.fail[i] + 1, m - i + 1);
    			// cerr << "addedge(rev) " << m - kmp_rev.fail[i] + 1 << " " << m - i + 1 << endl;
    		}
    	}
    	for (int i = 1; i <= m; ++i) {
    		if (!tree.fa[i]) {
    			tree.dfs1(i);
    			tree.dfs2(i, i);
    		}
    	}
    	for (int i = m; i >= 1; --i) {
    		if (!tree_rev.fa[i]) {
    			tree_rev.dfs1(i);
    			tree_rev.dfs2(i, i);
    		}
    	}
    	
    	DSU.build(m);
    	for (int i = n - K; i >= K; --i) {
    		if (kmp.match[i] != 0) {
    			int u = kmp.match[i];
    			// cerr << "cover " << u << " " << i << endl;
    			cover_anc(u, i);
    		}
    	} // 倒序添加, 预处理出每个位置第一次被覆盖是在什么时间
    	
    	DSU.get_res(first_cov_tim);
    	for (int i = 1; i <= m; ++i) {
    		if (tree_rev.rev[i] != 1) {
    			arr[i] = first_cov_tim[tree.dfn[tree_rev.rev[i] - 1]];
    			// cerr << arr[i] << endl;
    		}
    	}
    	RMQ.build(arr, m);
    	
    	for (int i = K + 1; i <= n - K + 1; ++i) {
    		if (kmp_rev.match[n - i + 1] != 0) {
    			int u = m - kmp_rev.match[n - i + 1] + 1;
    			// cerr << "query " << u << " " << i << endl;
    			assert(u != 1);
    			query_anc(u, i);
    		}
    	}
    	cout << "No" << endl;
    	return 0;
    }
    
  • 相关阅读:
    POJ 1003 解题报告
    POJ 1004 解题报告
    POJ-1002 解题报告
    vi--文本编辑常用快捷键之光标移动
    常用图表工具
    September 05th 2017 Week 36th Tuesday
    September 04th 2017 Week 36th Monday
    September 03rd 2017 Week 36th Sunday
    September 02nd 2017 Week 35th Saturday
    September 01st 2017 Week 35th Friday
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/14146557.html
Copyright © 2011-2022 走看看