Problem L. ZZUPC

本来以为是三月末甚至四月初才面试,所以当时学弟找我打校赛我就答应了(我说小概率时间可能会冲突,他说没事还有另一个大爹然后发现是我队友,没绷住 )。结果校赛时间定在21号,我20号晚上收到的面试通知,要我24号就面试,没招了。不过不是硬性冲突,就当是给机试练练手,所以我就还是上了。

这场赛时做起来不太顺,可能是太长时间没打所以手比较生。反正罚时吃的比较多,最后憋了两道题也没憋出来,才弄了个校第二,没招了。其实好像也不冤,毕竟我们队俩主力,还一年没认真训练了,而大三学弟队是经过大二一整年心无旁骛的训练的,所以水平没准比我们还强不少。不过就算同时期,也肯定是比不过我们的学长他们了,我刚入学那会真是看神仙打架,现在学长们都走了,哎感慨万千。

今天刚回,趁着还有点记忆说说 L 题,这题真不难,放大二的时候我们队估计直接秒了,气的我队友(还有一个在出题组,当天在当志愿者)指着我俩鼻子骂:"你们忘本了知不知道?"(他说这是当时我们进实验室那场选拔赛的题,但是我真没啥记忆了)。


Problem L. ZZUPC

  • Input file: standard input
  • Output file: standard output
  • Time limit: 2 second
  • Memory limit: 256 mebibytes

Zerc 在「浪潮杯」郑州大学第 17 届大学生程序设计竞赛 / The 17th ZZUPC 开始前向服务器祈祷。

突然,屏幕上出现了一个长度为 nnn 的仅由大写英文字母 Z U C P 组成的字符串 SSS。

下面还有一行文字:If you can find the maximum length of the lucky string, then I can bless this competition to go smoothly.

定义字符串 TTT 为幸运字符串 ,当且仅当:TTT 的字符可以通过重新排列,变成 (ZZUPC)×k(ZZUPC) \times k(ZZUPC)×k(即 kkk 个 ZZUPC 字符串首尾拼接而成,kkk 为非负整数)。

例如:

  • ZZUPC 是幸运字符串(k=1k=1k=1);
  • ZZUPCPCZZUPC 是幸运字符串(k=2k=2k=2);
  • ZUPCPCZ 可以重新排列为 ZZUPC,因此也是幸运字符串(k=1k=1k=1);
  • ZZUCP 无法重新排列为 ZZUPC × k,因此不是幸运字符串。

现在给定字符串 SSS,请你求出 SSS 的所有子串中,最长的幸运字符串的长度。

子串 (Substring) :若字符串 ttt 可以表示为 sss 中一段连续的字符序列,则称 ttt 是 sss 的子串。

形式化地,若存在 1≤l≤r≤∣s∣1 \le l \le r \le |s|1≤l≤r≤∣s∣,使得 t=slsl+1...srt = s_l s_{l+1} \dots s_rt=slsl+1...sr,则 ttt 是 sss 的子串。

例如:s=abcdes = abcdes=abcde,则 bcdade 均为其子串。而 ae 不是其子串。


Input

第一行输入一个整数 TTT (1≤T≤51 \le T \le 51≤T≤5),表示测试用例的数量。

对于每组测试样例:

  • 第一行一个整数 nnn (1≤n≤2×1051 \le n \le 2 \times 10^51≤n≤2×105),代表字符串长度;
  • 第二行一个长度为 nnn 的字符串 SSS,且仅包含字符 Z、U、C、P。

Output

对于每个测试用例,输出一个整数,表示最长的幸运字符串的长度。


Examples

输入1

cpp 复制代码
3
6
ZZUPC
8
ZZZUUCCP
13
ZZZZZUCPCUCPC

输出1

cpp 复制代码
6
0
12 

Note

  • 第一个测试用例:字符串本身就是 ZZUPC,长度为 6,对应 k=1k=1k=1。
  • 第二个测试用例:不包含幸运字符串。
  • 第三个测试用例:第 2 到 13 字符可以通过重组得到 ZZUPCPCZZUPC,故幸运字符串的最大长度为 12。

思路:

一开始的思路是:枚举最长的幸运字符串长度,对每种字符枚举右端点,找左端点的位置区间,然后四种字符的合法区间取交集。但是这个幸运字符串长度是不满足单调性的(即存在长的幸运字符串,不一定存在短的幸运字符串),因此无法二分答案。这样无论如何都无法做到低于 O(n2)O(n^2)O(n2) 的时间复杂度,何况还要找四种字符的左端点位置区间的交集。

正解是这样的:对一个长为 6k6k6k 的幸运字符串,它有 2k2k2k 个 Z, kkk 个 U, kkk 个 P, 2k2k2k 个 C。不妨先看 UP如果我们边处理边存储 前缀中 P 减去 U 的个数,那么我们就可以在 O(n)O(n)O(n) 的时间,找到对所有右端点,满足前缀中 P 减去 U 的个数相同的所有左端点

仔细想想其实也挺显然的,本质是把两种字符的相对数量用前缀和进行维护 。证明也很显然,不妨设 uiu_{i}ui 表示字符串 1∼i1\sim i1∼i 前缀中 uuu 的个数,同理 pip_ipi。那么有:ur−ul−1=pr−pl−1u_r-u_{l-1}=p_r-p_{l-1}ur−ul−1=pr−pl−1ur−pr=ul−1−pl−1u_r-p_r=u_{l-1}-p_{l-1}ur−pr=ul−1−pl−1 这样做的好处是,查区间和会被转化为对一个前缀和,查找另一个前缀和。如果我们边处理前缀和,并以当前位置的前缀和为区间右端点,找前面已经处理出来的左端点(前面的前缀和可以用哈希表或者查找树进行处理),一遍遍历就可以找到所有情况。

所以做了 P-U 的前缀和,同理我们可以维护 Z-2UC-2U 的前缀和,做一遍前缀和这个题就做完了。

所以我们现在只需要再做一个三元组的哈希,然后把哈希扔进哈希表里就行了。

代码:

没有OJ所以不能保证代码正确

cpp 复制代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <unordered_map>
using namespace std;
const int maxn=2e5+5;
typedef long long ll;

int n;
string str;
unordered_map<ll,int> mp;

ll s2i(array<int,3> state){
	ll pw=2*maxn;
	return (state[0]+maxn)*pw*pw+
			(state[1]+maxn)*pw+
			(state[2]+maxn);
}

void solve(){
	cin>>n>>str;
	array<int, 3> state{0,0,0};//Z-2U C-2U P-U
	mp.clear();
	mp.insert(make_pair(s2i(state),-1));
	
	int ans=0;
	for(int i=0;i<str.length();i++){
		char c=str[i];
		if(c=='U'){
			state[0]-=2;
			state[1]-=2;
			state[2]-=1;
		}
		else if(c=='Z')state[0]+=1;
		else if(c=='C')state[1]+=1;
		else if(c=='P')state[2]+=1;
		
		ll val=s2i(state);
		if(mp.find(val)!=mp.end())ans=max(i-mp[val],ans);
		else mp.insert(make_pair(val,i));
	}
	
	cout<<ans<<endl;
}


int main(){
	cin.tie(0)->sync_with_stdio(false);
	int T=1;
	cin>>T;
	while(T--)solve();
	return 0;
} 
/*
3
6
ZZUCPC
8
ZZZUUCCP
13
ZZZZZUCPCUCPC

*/
相关推荐
elseif1233 小时前
出题团招人【ETOI_】
c++
梯度下降中3 小时前
LoRA原理精讲
人工智能·算法·机器学习
IronMurphy3 小时前
【算法三十一】46. 全排列
算法·leetcode·职场和发展
czlczl200209253 小时前
力扣1911. 最大交替子序列和
算法·leetcode·动态规划
第二只羽毛3 小时前
C++ 高并发内存池1
大数据·开发语言·c++·开源
不想看见4043 小时前
C++/Qt 实习岗位深度解析【结合一次研发实习谈感受】
开发语言·c++·qt
靴子学长4 小时前
Decoder only 架构下 - KV cache 的理解
pytorch·深度学习·算法·大模型·kv
王老师青少年编程4 小时前
信奥赛C++提高组csp-s之组合数学专题课:鸽巢原理详解及案例实践
c++·组合数学·信奥赛·抽屉原理·csp-s·提高组·鸽巢原理
寒秋花开曾相惜4 小时前
(学习笔记)3.8 指针运算(3.8.3 嵌套的数组& 3.8.4 定长数组)
java·开发语言·笔记·学习·算法