CF961F k-substrings(哈希 + 二分 + 线段树)
哈希 + 二分 + 线段树
首先需要转变一下角度,容易发现如果按每个 \(k\) 计算答案,会计算多次相同答案的贡献。于是从答案对子串的贡献入手,枚举答案。因为 \(k\) 子串中心对称,于是 \(t\) 的中心也中心对称,枚举前缀的中心为 \(i\),那么对应后缀的中心为 \(n-i+1\),现在需要找到前缀等于后缀的最长半径。
显然可以二分,并且判断字符串相等可以用字符串哈希解决。那么假如半径为 \(r\),那么从产生贡献的最左端 \(i-r+1\) 出发,贡献依次是 \(2r-1,2r-3\cdots 1\),最右端为 \(i\)。 现在需要区间维护等差数列,单点查询。
找个数据结构维护。观察到所有等差数列的差值都为 \(2\),于是有个 trick:对于每个 \(j\in [i-r+1,i]\),贡献写成 \(2r-1+2(i-r+1)-2j\),这样每个等差数列的贡献就是前面的部分,后面的 \(2j\) 共有,只需要区间修改即可,都可以用线段树维护。
复杂度 \(O(n\log n)\)。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10, p = 20242024, mod = 1234567891;
int n;
std::string s;
i64 hsh[N], pw[N];
bool check(int x, int i, int j) {
int l1 = i - x + 1, r1 = i + x - 1, l2 = j - x + 1, r2 = j + x - 1;
if(l1 < 1 || r2 > n || l2 < 1 || l2 > n) return 0;
i64 ret1 = (hsh[r1] - hsh[l1 - 1] * pw[2 * x - 1] % mod + mod) % mod, ret2 = (hsh[r2] - hsh[l2 - 1] * pw[2 * x - 1] % mod + mod) % mod;
if(ret1 == ret2) return 1;
return 0;
}
struct SEG {
int max, lzy;
} t[N << 2];
void pushup(int u) {
t[u].max = std::max(t[u << 1].max, t[u << 1 | 1].max);
}
void mdf(int u, int x) {
t[u].max = std::max(t[u].max, x);
t[u].lzy = std::max(t[u].lzy, x);
}
void pd(int u) {
if(!t[u].lzy) return;
mdf(u << 1, t[u].lzy), mdf(u << 1 | 1, t[u].lzy);
t[u].lzy = 0;
}
void upd(int u, int l, int r, int L, int R, int x) {
if(L <= l && r <= R) {
mdf(u, x);
return;
}
int mid = (l + r) >> 1; pd(u);
if(L <= mid) upd(u << 1, l, mid, L, R, x);
if(R > mid) upd(u << 1 | 1, mid + 1, r, L, R, x);
pushup(u);
}
int qry(int u, int l, int r, int x) {
if(l == r) return t[u].max;
int mid = (l + r) >> 1; pd(u);
if(x <= mid) return qry(u << 1, l, mid, x);
return qry(u << 1 | 1, mid + 1, r, x);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> s;
s = '#' + s;
for(int i = 1; i <= n; i++) {
hsh[i] = (hsh[i - 1] * p + s[i]) % mod;
}
pw[0] = 1;
for(int i = 1; i <= n; i++) pw[i] = pw[i - 1] * p % mod;
for(int i = 1; i <= (n + 1) / 2; i++) {
int j = n - i + 1, l = 1, r = n, ret = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid, i, j)) l = mid + 1, ret = mid;
else r = mid - 1;
}
if(ret) {
l = i - ret + 1, r = i;
if(j + ret - 1 - l + 1 == 2 * ret - 1) continue;
upd(1, 1, n, l, r, 2 * ret - 1 + 2 * l);
}
}
for(int i = 1; i <= (n + 1) / 2; i++) {
int ret = qry(1, 1, n, i) - 2 * i;
std::cout << (ret > 0 ? ret : -1) << " \n"[i == (n + 1) / 2];
}
return 0;
}

浙公网安备 33010602011771号