首页 > 其他分享 >[LNOI2022] 串

[LNOI2022] 串

时间:2023-09-25 17:33:43浏览次数:61  
标签:LNOI2022 rk2 int ++ sa FL rk

题目链接

显然答案下界为 \(\lfloor\frac{n}{2}\rfloor\)。采用一种对着题意模拟的策略:假设我们初始的区间为 \([l,r]\),然后逐步向左平移,也就是:\([l,r],[l-1,r-2],[l-2,r-4],\dots\) 直到碰到边界(平移的次数 \(+1\) 就等于 \(m\))。显然 \(l\) 取 \(\lfloor\frac{n}{2}\rfloor\),\(r\) 取 \(n\) 时最优,也就是我们所说的答案下界。

之后考虑怎么尽量地延长这个平移的过程,不难发现:当我们平移到一个串,假设原串中后面还有出现过这个串,那么它就可以一直平移。直到到区间长度为 \(0\)。稍作推理易发现,假设我们的区间为 \([l,r]\),那么最终经过当前区间的最大 \(m\) 为 \(r-l+1+\lfloor\frac{n-r}{2}\rfloor\)。

直接构建后缀数组,枚举左端点,然后贪心统计贡献就行了(观察式子,会发现左端点固定右端点大的较优)。

代码实现中的初始化要额外注意。

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); ++i)
#define FR(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
const int N = 5e5 + 10;
struct SA{
	int n, m, c[N], sa[N], rk[N], rk2[N], h[N];
	char s[N];
	void rsort(){
		fill(c, c + m + 1, 0);
		FL(i, 1, n) c[rk[i]]++;
		FL(i, 1, m) c[i] += c[i - 1];
		FR(i, n, 1) sa[c[rk[rk2[i]]]--] = rk2[i];
	}
	void ssort(){
		FL(i, 1, n) rk[rk2[i] = i] = s[i];
		m = 126, rsort();
		for(int k = 1; k <= n && (k == 1 || m < n); k <<= 1){
			int t = 0;
			FL(i, n - k + 1, n) rk2[++t] = i;
			FL(i, 1, n) if(sa[i] > k) rk2[++t] = sa[i] - k;
			rsort(), copy(rk, rk + n + 1, rk2);
			rk[sa[1]] = m = 1;
			FL(i, 2, n){
				if(rk2[sa[i]] != rk2[sa[i - 1]])
					rk[sa[i]] = ++m;
				else if(rk2[sa[i] + k] != rk2[sa[i - 1] + k])
					rk[sa[i]] = ++m;
				else rk[sa[i]] = m;
			}
		}
		int k = 0; fill(h, h + n + 2, 0);
		FL(i, 1, n) if(rk[i] > 1){
			if(k) k--;
			while(s[i + k] == s[sa[rk[i] - 1] + k]) k++;
			h[rk[i]] = k;
		}
	}
	void build(char str[]){
		copy(str, str + strlen(str + 1) + 2, s);
		n = strlen(s + 1), ssort();
	}
} sa;
char s[N]; int ans;
void solve(){
	scanf("%s", s + 1), sa.build(s), ans = sa.n / 2;
	FL(i, 1, sa.n){
		int l = max(sa.h[i + 1], sa.h[i]);
		ans = max(ans, l + (sa.n - (sa.sa[i] + l - 1)) / 2);
	}
	printf("%d\n", ans);
}
int main(){
	int T; scanf("%d", &T);
	while(T--) solve();
}

标签:LNOI2022,rk2,int,++,sa,FL,rk
From: https://www.cnblogs.com/zac2010/p/17728407.html

相关文章

  • 洛谷 P8367 - [LNOI2022] 盒(组合数学)
    设\(a\)数组的前缀和为\(s_i\),\(b\)数组的前缀和为\(t_i\),那么根据模拟费用流或者贪心的思想,每一条边经过的次数即为\(|s_i-t_i|\),因此非常trivial的做法是转换贡献体,枚举每种方案下每条边被经过的次数,然后乘以\(w_i\)求和,具体来说:\[ans=\sum\limits_{i=1}^{n-1}\sum\l......
  • luogu P8367 [LNOI2022] 盒
    题面传送门比较厉害的题目。首先我们发现我们只需要计算\(i\)和\(i+1\)之间经过的货物数,也即设\(a\)的前缀和为\(Sum\),\(b\)的前缀和为\(c\),则\(i\toi+1\)......
  • P8368 [LNOI2022] 串 题解
    题目链接题目分析题目要求我们构造一个最长的\(T\)序列,我们首先从每个\(T_i\)入手,思考如何安排才能合法。容易观察到对于每个\(T_i\),合法的\(T_{i-1}\)有两种方......
  • 【题解】[LNOI2022] 盒
    题目分析:我们可以对每一条边单独计算贡献,这样会发现贡献很好算:\[ans=\sum_{i=0}^{n-1}w_i\sum_{j=0}^S|j-s_i|\binom{i+j-1}{i-1}\binom{S-j+n-i-1}{n......