BZOJ 2865 字符串识别 | 后缀数组 线段树
2018-01-05 09:40
381 查看
集训讲字符串的时候我唯一想出正解的题……
链接
题面
给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的、最短的、在原串中只出现过一次的子串。
题解
“只出现过一次”,想到后缀数组,后缀数组可以求出以第i位开头的最短的在原串中只出现过一次的子串——它的长度是min(height[rank[i]], height[rank[i] + 1) + 1。
所以我们枚举每个位置i,找到这个串,然后考虑它的贡献:
对于这个串之内的位置,答案可以用这个串的长度更新;
对于这个串右边的位置,串可以向右“延伸”直到包含该位置(延伸后的串显然也只出现过一次),所以答案可以用(该位置 - i + 1)来更新。
这两个分别用线段树维护即可。
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; #define enter putchar('\n') #define space putchar(' ') template <class T> void read(T &x){ char c; bool op = 0; while(c = getchar(), c > '9' || c < '0') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 500005, INF = 0x3f3f3f3f; int n, sa , rnk , buf1 , buf2 , buc , height ; int data[2][4*N], lazy[2][4*N], pos ; char s ; void pushdown(int h, int k){ if(lazy[h][k] == INF) return; lazy[h][k << 1] = min(lazy[h][k << 1], lazy[h][k]); lazy[h][k << 1 | 1] = min(lazy[h][k << 1 | 1], lazy[h][k]); data[h][k << 1] = min(data[h][k << 1], lazy[h][k]); data[h][k << 1 | 1] = min(data[h][k << 1 | 1], lazy[h][k]); lazy[h][k] = INF; } void modify(int h, int k, int l, int r, int ql, int qr, int x){ if(ql <= l && qr >= r){ data[h][k] = min(data[h][k], x); lazy[h][k] = min(lazy[h][k], x); return; } int mid = (l + r) >> 1; if(ql <= mid) modify(h, k << 1, l, mid, ql, qr, x); if(qr > mid) modify(h, k << 1 | 1, mid + 1, r, ql, qr, x); data[h][k] = min(data[h][k << 1], data[h][k << 1 | 1]); } void pushdown_all(int k, int l, int r){ if(l == r) return (void)(pos[l] = k); pushdown(0, k), pushdown(1, k); int mid = (l + r) >> 1; pushdown_all(k << 1, l, mid); pushdown_all(k << 1 | 1, mid + 1, r); } void suffix_sort(){ int m = 128, *x = buf1, *y = buf2; for(int i = 0; i <= m; i++) buc[i] = 0; for(int i = 1; i <= n; i++) buc[x[i] = s[i]]++; for(int i = 1; i <= m; i++) buc[i] += buc[i - 1]; for(int i = n; i; i--) sa[buc[x[i]]--] = i; for(int k = 1, p = 0; k <= n && p < n; k *= 2, m = p, p = 0){ for(int i = n - k + 1; i <= n; i++) y[++p] = i; for(int i = 1; i <= n; i++) if(sa[i] > k) y[++p] = sa[i] - k; for(int i = 0; i <= m; i++) buc[i] = 0; for(int i = 1; i <= n; i++) buc[x[y[i]]]++; for(int i = 1; i <= m; i++) buc[i] += buc[i - 1]; for(int i = n; i; i--) sa[buc[x[y[i]]]--] = y[i]; swap(x, y), x[sa[1]] = 1, p = 1; for(int i = 2; i <= n; i++) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p : ++p; } for(int i = 1; i <= n; i++) rnk[sa[i]] = i; for(int i = 1, k = 0; i <= n; i++){ if(rnk[i] == 1) continue; int j = sa[rnk[i] - 1]; if(k) k--; while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++; height[rnk[i]] = k; } } int main(){ scanf("%s", s + 1); n = strlen(s + 1); suffix_sort(); memset(data, INF, sizeof(data)); memset(lazy, INF, sizeof(lazy)); for(int i = 1; i <= n; i++){ int len = max(height[rnk[i]], height[rnk[i] + 1]); if(i + len <= n) modify(0, 1, 1, n, i, i + len, len + 1); if(i + len < n) modify(1, 1, 1, n, i + len + 1, n, 1 - i); } pushdown_all(1, 1, n); for(int i = 1; i <= n; i++) write(min(data[0][pos[i]], i + data[1][pos[i]])), enter; return 0; }
相关文章推荐
- [BZOJ2865]字符串识别 后缀自动机+线段树
- 【BZOJ-1396&2865】识别子串&字符串识别 后缀自动机/后缀树组 + 线段树
- 【bzoj2865】字符串识别 后缀自动机+线段树
- [后缀自动机 线段树] BZOJ 1396 识别子串 & BZOJ 2865 字符串识别
- BZOJ1396&2865 识别子串 【后缀自动机 + 线段树】
- 【BZOJ1396】识别子串&【BZOJ2865】字符串识别(后缀自动机)
- BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)
- 【BZOJ1396】识别子串&【BZOJ2865】字符串识别(后缀自动机)
- BZOJ 1396&&2865 识别子串[后缀自动机 线段树]
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
- [达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
- 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分
- [bzoj3473][bzoj3277][后缀数组]字符串
- bzoj 3277: 串 & bzoj 3473: 字符串【后缀自动机||后缀数组】
- 【BZOJ2865】字符串识别
- 【BZOJ4516】【Sdoi2016】生成魔咒 后缀数组 线段树
- BZOJ 3277: 串/ BZOJ 3473: 字符串 ( 后缀数组 + RMQ + 二分 )
- 字符串(马拉车算法,后缀数组,稀疏表):BZOJ 3676 [Apio2014]回文串
- [BZOJ1396]识别子串(后缀自动机+线段树)
- bzoj2865 字符串识别