哈希与哈希表
• 使用一个哈希函数将某个特定的数字变成另一个数字,这种操作称之为hash。 • 通常我们会以取模运算来作为哈希函数。 • 举例: hash(key)=key%23, 这样数组 [1 ,75,324] -> [1 ,6,2] • 如果哈希后得到的值相同,我们则可用该值建一个链表,把相同的值都放一起, 这样我们就可以得到一个哈希表。 核心代码
字符串hash
• 考虑这样一个问题:
• 有m个字符串,总长S。 q次询问两个字符串是否完全一样。 • 数据范围1 0^5。 优化做法: • 一个相对普适的做法是这样的: • 将这个字符串(假设只有小写字母)视为一个27进制数,将a看作1, b看作2,依此类推。 • 比如‘abca’看作1 *27^3+2*27^2+3*27+1。 • 但这样在字符串较大时数字会很大,存不下来。 • 一个欺骗方法是找一个较大的数P(最好是大质数),只记录转换后数字对P 取模的结果。 核心代码
哈希冲突
自然溢出
子串hash
哈希与回文串
例题:
大体思路: 设 L[i] 表示 S 中最小的下标,它满足从它开始的长度为 i 个字符是 T[1…i]. 同时还需要满足 L[i]+i >= k.(否则长度不够, 没法取这个字符串)。 没有满足条件的下标则记为 -1 . 同理,设 R[i] 表示 S 中最大的下标,满足以它结尾的前 i 个字符是 T[mi+1…m]. 同时还满足 R[i]-I <= n-k+1 .(否则右串的长度不够,没法取)。 没有满足条件的下标则记为 -1 . 求 L 和 R 数组需要用到扩展KMP算法。 扩展KMP目标是求2个串的最长公共前缀。 假设我们求得 extend[i] 表示 S[i...n] 和 T 的最长公共前缀。 要求 L[i] 即 找到 extend[x]>=i 且 x+i>=k. 时间复杂度O(n^2). 一种优化的方案是:先对extend数组排序,然后从大到小枚举 i,每次把 extend[x]>=i 的 x 加入一个集合。 询问的就是 x>=k-i 的最小的 x。可以用一个 set 在O(logn) 时间内完成。 (s.lower_bound(k-i) )。 翻转 S 和 T,同理求出 R 数组。
// 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 << " \n"[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 << " \n"[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; }