CodeForces - 961F k-substrings

题意:

给定一个长度为 n n n 的字符串 s s s,对每个 k − s u b s t r i n g ( s k , s k + 1 , ⋯   , s n − k + 1 ) k-substring(s_k, s_{k + 1}, \cdots, s_{n - k + 1}) ksubstring(sk,sk+1,,snk+1) 求最长的奇数长度 b o r d e r border border ( n ≤ 1 0 6 ) (n \leq 10^6) (n106)

链接:

https://codeforces.com/problemset/problem/961/F

解题思路:

对单个字符串,可以 k m p kmp kmp 求出最长 b o r d e r border border,但是这里是 ⌈ n 2 ⌉ \lceil\frac{n}{2}\rceil 2n 个字符串。最长公共前后缀无法直接二分前缀长度来求,但是可以通过枚举前缀的中点 i i i,相应后缀的中点为 n − i + 1 n - i + 1 ni+1,可以二分最大的 x x x,即得到前缀中点为 i i i 的最长公共前后缀。可以用哈希来快速判断。

参考代码:
#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e6 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
 
struct Hash{

	const static int md0 = 1e9 + 7, md1 = 1e9 + 9;
	int h0, h1;
	Hash(int h0 = 0, int h1 = 0) : h0(h0), h1(h1) {}
	Hash operator + (const int &o) const { return Hash((h0 + o) % md0, (h1 + o) % md1); }
	Hash operator * (const int &o) const { return Hash((h0 * 1ll * o) % md0, (h1 * 1ll * o) % md1); }
	Hash operator + (const Hash &o) const { return Hash((h0 + o.h0) % md0, (h1 + o.h1) % md1); }
	Hash operator - (const Hash &o) const { return Hash((h0 - o.h0 + md0) % md0, (h1 - o.h1 + md1) % md1); }
	Hash operator * (const Hash &o) const { return Hash((h0 * 1ll * o.h0) % md0, (h1 * 1ll * o.h1) % md1); }
	bool operator == (const Hash &o) const { return h0 == o.h0 && h1 == o.h1; }
} has[maxn], pw[maxn], base = {131, 133};
vector<pii> G[maxn];
multiset<int> st;
char s[maxn];
int n;

Hash getH(int l, int r){

	return has[r] - has[l - 1] * pw[r - l + 1];
}

int judge(int x, int mid){

	int lx = x - mid, rx = x + mid;
	int y = n - x + 1;
	int ly = y - mid, ry = y + mid;
	if(lx < 1 || rx > n || ly < 1 || ry > n || rx - lx + 1 == n) return 0;
	return getH(lx, rx) == getH(ly, ry);
}

int main() {
 
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> n >> s + 1;
	pw[0] = {1, 1};
	for(int i = 1; i <= n; ++i){

		pw[i] = pw[i - 1] * base;
		has[i] = has[i - 1] * base + s[i];
	}
	for(int i = 1; i <= n / 2; ++i){

		int l = 0, r = n, mid, ret = -1;
		while(l <= r){

			mid = gmid;
			if(judge(i, mid)) l = mid + 1, ret = mid;
			else r = mid - 1;
		}
		if(ret == -1) continue;
		G[i - ret].pb({i, 1});
		G[i + 1].pb({i, -1});
	}
	int lim = (n + 1) / 2;
	for(int i = 1; i <= lim; ++i){

		for(auto &e : G[i]){

			if(e.second == 1) st.insert(e.first);
			else st.erase(st.find(e.first));
		}
		if(!sz(st)) cout << "-1 ";
		else{

			int mx = *st.rbegin();
			int ret = 2 * mx - 2 * i + 1;
			cout << ret << " ";
		}
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值