CF1550E Stringforces 题解

Codeforces
Luogu

P.S.

给定一个长度为 \(n\) 字符集为 \(K\) 的字符串,其中有 ?,你需要把 ? 填成 \(1..K\) 中的一个字符。
最大化每种字符最长连续子串的最小值,输出这个最大值。
\(n\le 10^5,K\le 17\)

Solution.

最大化最小值,直接二分答案,转化成每种字符至少有一个长度为 \(x\) 的连续子串。
然后看到 \(K\le 17\),那最后应该是个状压,\(dp_S\) 表示 \(S\) 中字符已满足的最小长度。
考虑怎么状态转移,我们枚举接下来应该填哪种字符,然后转移,复杂度是 \(O(2^K\times K\times F(x))\)
其中 \(F(x)\) 是转移复杂度,看式子基本是 \(O(1)\)\(O(\text{poly}(\log))\) 了。
我们考虑直接记录,复杂度是 \(O(n\times K)-O(1)\) 的,可过。
考虑记录第 \(i\) 个位置后面能通过连续的 \(j\) 字符扩展到那里。
这个显然一遍就可以了,还需要记录一下前缀和来快速判断区间是否全都是 ? 或当前字符。
然后就做完了,总复杂度 \(O(n\times K+2^K\times K)\)

Coding.

点击查看垃圾代码
//是啊……你就是那只鬼了……所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=1;
	for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	if(f) x=-x;
}/*}}}*/
const int N=200005;int n,K,sq[N],sc[N][17],ps[N][17],dp[1<<17];char ch[N];
inline char chk(int x)
{
	memset(ps[n+2],0x3f,sizeof(ps[n+2]));for(int k=0;k<K;k++) for(int i=n+1;i;i--)
		ps[i][k]=(i+x-1<=n&&sq[i+x-1]-sq[i-1]+sc[i+x-1][k]-sc[i-1][k]==x)?i+x-1:ps[i+1][k];
	dp[0]=1;for(int i=1;i<(1<<K);i++) dp[i]=n+2;
	for(int S=1;S<(1<<K);S++) for(int i=0;i<K;i++)
		if((S>>i)&1) dp[S]=min(dp[S],ps[dp[S^(1<<i)]][i]+1);
	return dp[(1<<K)-1]<=n+1;
}
int main()
{
	read(n),read(K),scanf("%s",ch+1);for(int i=1;i<=n;i++) sq[i]=sq[i-1]+(ch[i]=='?');
	for(int i=1;i<=n;i++) {memcpy(sc[i],sc[i-1],sizeof(sc[i]));if(ch[i]!='?') sc[i][ch[i]-'a']++;}
	int l=0,r=n/K,rs=0;while(l<=r) {int md=(l+r)>>1;if(chk(md)) rs=md,l=md+1;else r=md-1;}
	return printf("%d\n",rs),0;
}
posted @ 2021-07-28 18:05  Peal_Frog  阅读(63)  评论(0编辑  收藏  举报