首页 > 其他分享 >【题解】CF1550E Stringforces

【题解】CF1550E Stringforces

时间:2023-11-27 21:45:36浏览次数:35  
标签:int 题解 字母 mid jump 枚举 CF1550E Stringforces dp

标签:DP \(B^+\)

阅读须知:本题解较为详细地讲述的该题解法的思路和来龙去脉,但篇幅较长,请耐心阅读。


Step 1 从题面获取信息

我们考虑,因为最大值最小,所以我们首先想到二分答案。

然后我们又看到 \(k \leq 17\) 这个限制,所以会想到可能是关于一个 \(2^k\) 之类的复杂度。

以上就是我们第一步从题目的数据范围中挖掘出来的一些思路。


Step 2 从暴力入手进行优化

我们二分答案 \(mid\) 之后,就只需要判断答案 \(mid\) 的可行性了,也就是说我们要判断是否每一种字母都能有 \(mid\) 长度的子串。

对于判断正确性显然有一种非常简单的暴力,就是枚举字母所确定的子串所出现的相对顺序,然后暴力 check 是否可行,这是 \(O(n\times k!)\) 的,但是显然不能通过这道题。

① 状态枚举的优化

这时,我们复杂度瓶颈在于花去太多时间在出现顺序的枚举(\(n!\)),而我们上文分析到的 \(2^k\) 显然他的一个很好的转化的终点。

那么为什么能转化呢,又如何转化呢?

为什么能:我们发现每次加进去一种字母的长为 \(mid\) 的段时,并不需要知道之前的字母的出现的顺序到底是什么,而我们的 \(n!\) 的暴力算法的顺序枚举显然会多出许多不必要的信息的枚举,这显然给了我们代码时间复杂度的优化空间。

如何转化:我们可以设 \(dp_S\) 表示让集合 \(S\) 中的所有字母都满足有长度为 \(mid\) 的子串的最短的前缀(显然前缀越短剩下的字母放完的可能性才更大)。

温馨提示:这里的前缀串在记录时为开区间

这下我们就将状态个数优化到了 \(2^k\),\(O((n+k)2^k\log n)\) 是不能通过本题的,但是状态显然是已经优化到了极致,所以我们只能从转移上下手,尝试把 \(n\) 给干掉。


② 转移的优化

我们发现每个位置的转移的显然不需要依赖于当前的整个状态(数量:\(2^k\)),只需要依赖于当前需要加入哪种字母(数量:\(k\))进入集合,所以说我们可以将原本的 \(n2^k\) 的转移变成 \(nk\) 。

显然地,你需要预处理转移数组 \(jump_{i,j}\) 表示原来的前缀串以 \(i\) 结束,满足入第 \(j\) 种字母的要求后,新的前缀串的最小的右端点的位置,这时需要倒序枚举(后面有解释),转移如下:

\[jump_{i,j} = \begin{cases} i+len & \forall i\leq k < i+len,s_j = 'a'+j~||~s_j = '?'\\ jump_{i+1,j} & otherwise \end{cases} \]

而对于第一种转移的限制条件的判定,我们可以动态维护除了这一种字母、其他字母最左边出现的位置,这显然需要倒序枚举。

Step 3 思考实现细节并敲出代码

code:

#include<bits/stdc++.h>
using namespace std;
const int NN = 2e5 + 8,MM = 20;
int dp[1 << MM];
int n,k;
char s[NN];//字符串
int jump[NN][MM];
int minp[MM];//第i种字母最左边出现的位置
int res[MM];//除第i种字母以外的其他字母最左边出现的位置
bool solve(int len){
	memset(dp,0x3f,sizeof(dp));
	memset(res,0x3f,sizeof(res));//初始化
	for(int i = 0; i < k; ++i) minp[i] = n+1,jump[n+1][i] = n+2,jump[n+2][i] = n+2;//限定边界
	for(int i = n; i >= 1; --i){
		if(s[i] != '?'){//更新res
			minp[s[i]-'a'] = i;
			for(int j = 0; j < k; ++j){
				if(s[i]-'a' == j) continue;
				res[j] = i;
			}
		}
		for(int j = 0; j < k; ++j){//转移
			if(res[j] >= i+len) jump[i][j] = min(n+2,i+len);
			else jump[i][j] = jump[i+1][j];
		}
	}
	dp[0] = 1;
	for(int i = 1; i < (1 << k); ++i){//DP转移
		for(int j = 0; j < k; ++j)if(i >> j & 1){
			dp[i] = min(jump[dp[i^(1 << j)]][j],dp[i]);
		}
	}
	return dp[(1 << k)-1] <= n+1;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin >> n >> k;
	cin >> s+1;
	int l = 0,r = n,ans = 0;
	while(l <= r){
		int mid = (l + r) / 2;
		if(solve(mid)) l = mid + 1,ans = mid;
		else r = mid - 1;
	}
	printf("%d",ans);
}

标签:int,题解,字母,mid,jump,枚举,CF1550E,Stringforces,dp
From: https://www.cnblogs.com/rickylin/p/solution_CF1550E.html

相关文章

  • ABC330 E Mex and Update 题解
    LinkABC330EMexandUpdateQuestion给一个数组\(a\),有\(Q\)次修改每次把\(a_i\)改成\(x\)问每次修改后,不在\(a\)数组中的最小非负数时多少Solution记录每个\(a_i\)出现的次数\(num\)每个修改操作可以看成时先删除,后添加用set维护为\(num_x=0\)的\(x\)......
  • UVA11275 3D Triangles 题解
    LinkUVA112753DTrianglesQuestion给你三维空间中的两个三角形,请判断它们是否有公共点。Solution如果在三维空间中相交,那么,肯定有一个三角形的某一条边穿过了另外一个三角形Code#include<bits/stdc++.h>usingnamespacestd;constdoubleeps=1e-9;structPoint3{......
  • SP1557 GSS2 - Can you answer these queries II 题解
    SP1557GSS2-CanyouanswerthesequeriesII更好的阅读体验扫描线。把询问挂在右端点上,扫描右端点,纵轴仍为序列维。对于这种出现多次的数只算一次的,记\(pre_i\)表示\(i\)这个值上一次的出现位置,套路化的可以强制让出现多次的在\(pre_i<l\wedgei\)统计,用二维线段树状......
  • CF1900 D Small GCD 题解
    LinkCF1900DSmallGCDQuestion定义\(f(x,y,z)=\gcd(a,b)\),其中\(a,b\)为\(x,y,z\)中较小的那两个数给出数组\(a\),求\[\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n\sum\limits_{k=j+1}^nf(a_i,a_j,a_k)\]三个求和符号本质上就是选数组\(a\)中的三个数,也就是说,数......
  • P3375 【模板】KMP( 普及/提高− ) 题解
    题目传送门思路:首先我们要学习一下\(KMP\)算法,不会的可以看这个大佬的文章那么我们就直接开始讲思路了。针对于每一位,\(kmp\)算法已经预处理出了一个对应\(kmp\)数组的单元,映射着如果此位失配,它可能的最靠后的一个重新开头是哪一个。让我们举一个例子:假如让\(aaab\)与......
  • P9447 [ICPC2021 WF] Spider Walk 题解
    更好的阅读体验很有意思的一道题。设\(f_i\)表示第\(i\)根线的答案,首先有一个关键结论:任意两根相邻的线答案只差一定小于\(1\)。原因显然,可以在无限远的地方加一根线来构造。该结论可以扩展一下,对于距离为\(d\)的两根线,答案之差不会超过\(d\)。考虑进行倒着加线,考虑加......
  • CF1900 C Anji's Binary Tree 题解
    LinkCF1900CAnji'sBinaryTreeQuestion给出一个树,每个节点上有一个字母L/R/U,从\(1\)号根节点开始,L表示接下来走到左节点,R表示接下来走到右节点,U表示接下载走到父节点问,最少修改几个节点上的字母使得从根节点走到叶子节点Solution定义\(F_x\)表示从\(x\)走到叶......
  • P7626 [COCI2011-2012#1] MATRIX( 普及/提高− ) 题解
    题目传送门思路:首先思考暴力,\(O(n^4)\)的时间复杂度,不行。那么我们这里就要运用到一点前缀和的知识了。我们可以用前缀和对两条对角线进行计数。每个点有两个对角线运算。差不多是\(O(n^2)\)到\(O(n^3)\)的时间复杂度。而\(n\leq400\)稳过。Code:#include<bits/stdc......
  • 复旦大学数学学院23级高等代数I期中考试精选大题解答
    四、求解下列线性方程组,其中$a_1,\cdots,a_n,b$为参数且$\sum\limits_{i=1}^na_i\neq0$:$$\begin{cases} (a_1+b)x_1+a_2x_2+a_3x_3+\cdots+a_nx_n=0,\\ a_1x_1+(a_2+b)x_2+a_3x_3+\cdots+a_nx_n=0,\\ a_1x_1+a_2x_2+(a_3+b)x_3+\cdots+a_nx_n=0,\\ \hfill\cdots\cdots......
  • CF1900 B Laura and Operations 题解
    LinkCF1900BLauraandOperationsQuestion给出\(1,2,3\)的个数\(a,b,c\)可以分别减少两个不同的数,增加一个与两个数都不同的数问,是否能经过一些操作使得就剩下\(1\)或\(2\)或\(3\)Solution先考虑\(1,2,3\)其实是等价的,所以我们只需要考虑把\(2,3\)全都变成......