【ICPC】The 2024 ICPC Kunming Invitational Contest I

Left Shifting 2

#双指针 #贪心 #数据结构

题目描述

Given a string consisting of lower-cased English letters, we say the string is beautiful if all neighboring characters are different. For example, icpc and kunming are beautiful but hello is not, because its 3 3 3-rd and 4 4 4-th characters are the same.

Given a string S = s 0 s 1 ⋯ s n − 1 S = s_0s_1\cdots s_{n-1} S=s0s1sn1 of length n n n consisting of lower-cased English letters, let f ( S , d ) f(S, d) f(S,d) be the string obtained by shifting S S S to the left d d d times. That is f ( S , d ) = s ( d + 0 )   m o d   n s ( d + 1 )   m o d   n ⋯ s ( d + n − 1 )   m o d   n f(S, d) = s_{(d+0)\bmod n}s_{(d+1)\bmod n}\cdots s_{(d+n-1)\bmod n} f(S,d)=s(d+0)modns(d+1)modns(d+n1)modn.

Let g ( S , d ) g(S, d) g(S,d) be the smallest number of operations needed to make string f ( S , d ) f(S, d) f(S,d) beautiful. In each operation, you can change one character in f ( S , d ) f(S, d) f(S,d) to any lower-cased English letter.

Find a non-negative integer d d d which minimizes g ( S , d ) g(S, d) g(S,d) and output the minimized value.

输入格式

There are multiple test cases. The first line of the input contains an integer T T T indicating the number of test cases. For each test case:

The first and only line contains a string s 0 s 1 ⋯ s n − 1 s_0s_1\cdots s_{n-1} s0s1sn1 ( 1 ≤ n ≤ 5 × 1 0 5 1 \le n \le 5 \times 10^5 1n5×105) consisting only of lower-cased English letters.

It’s guaranteed that the sum of n n n of all test cases will not exceed 5 × 1 0 5 5 \times 10^5 5×105.

输出格式

For each test case, output one line containing one integer, indicating the smallest possible g ( S , d ) g(S, d) g(S,d).

样例 #1

样例输入 #1

3
abccbbbbd
abcde
x

样例输出 #1

2
0
0

解题思路

首先对于这种循环移动的问题,我们都可以将它拷贝一份拼接到最后,长度变为 2 ∗ n 2*n 2n

然后此问题就转换成了:找到一个长度为 n n n的窗口,使修改成合法序列的次数最少。

容易发现,对于连续的 m m m长度的相同的字符,贪心的修改 ⌊ m / 2 ⌋ \lfloor m/2 \rfloor m/2次即可,这个窗口对答案的贡献就是 ∑ ⌊ m i / 2 ⌋ \sum\lfloor m_i/2 \rfloor mi/2,即窗口内所有的连续长度 / 2 /2 /2的贡献。

容易想到使用双指针来维护这个窗口,考虑指针移动时怎么更新答案。

我们可以对每个字符都映射一个新的下标 i d id id,然后使用 s t d : : m a p < s t d : : p a i r < c h a r , i n t > > std::map<std::pair<char,int>> std::map<std::pair<char,int>>,其中 c h a r char char表示字符,而 i n t int int表示 i d − i id - i idi i i i为原始下标,这样就能保证这个字符的连续增加的长度了。

那么就很简单了,动态更新最小答案即可。

时间复杂度在 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

代码

void solve() {
	std::string s;
 
	std::cin >> s;
	int n = s.size();
	s = s + s;
	s = " " + s;
 
	std::vector<std::pair<char, int>>a(2 * n + 1);
	std::vector<int>id(26);
 
	for (int i = 1; i <= 2 * n; ++i) {
		a[i] = { s[i],++id[s[i] - 'a'] };
	}
 
	std::map<pii, int>mp;
 
	int res = 0;
	auto add = [&](int idx)->void {
		int s = mp[{a[idx].first, a[idx].second - idx}]++;
 
		res -= s / 2;
		res += (s + 1) / 2;
 
		};
 
	auto del = [&](int idx)->void {
		int s = mp[{a[idx].first, a[idx].second - idx}]--;
 
		res -= s / 2;
		res += (s - 1) / 2;
 
		};
 
	for (int i = 1; i <= n; ++i) {
		add(i);
	}
 
	int ans = res;
 
	for (int R = n + 1; R <= 2 * n; ++R) {
		int L = R - n;
		del(L);
		add(R);
 
		ans = std::min(ans, res);
	}
 
	std::cout << ans << "\n";
}
signed main() {
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	std::cout.tie(0);
 
	int t = 1;
	std::cin >> t;
 
	while (t--) {
		solve();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值