[Codeforces938F][DP]Erasing Substrings

本文探讨了一种算法,用于解决在特定操作下寻找字典序最小串的问题。通过状态压缩和字典序性质的应用,算法实现了高效的状态转移和最小串的确定,最终输出最优解。

翻译

给你一个长度为n的串
K次操作(K==⌊log⁡2n⌋\lfloor \log_2n\rfloorlog2n)
第i次操作去掉长度为2i−12^{i-1}2i1的串
求最后剩下的字典序最小的串
n<=5000

题解

显然我们可以找到一种方案
使得删去的串在原串中没有相交的情况
f[i][j]f[i][j]f[i][j]表示删去后剩下的串长度为i,删除掉的长度状态为j
第二维是状压
思考我们里面要写什么
如果压一个字符串的话…暴力比较字典序复杂度n3log⁡nn^3\log nn3logn
运用字典序的性质
如果对于一个f[i][j],f[i][k]f[i][j],f[i][k]f[i][j],f[i][k]且有f[i][j]&lt;f[i][k]f[i][j]&lt;f[i][k]f[i][j]<f[i][k]
显然f[i][k]f[i][k]f[i][k]这个状态荒废了
如此可以改为
f[i][j]f[i][j]f[i][j]表示… 是否是长度i的字典序最小串
转移的话扫一遍状态确定下一个字符
我们还要在当前状态删一些数为后面做准备
显然这些状态下,只要他们的某个子集是最小串,他们就是合法的
用一个高维前缀和优化即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
bool ok[5005][(1<<15)+5];
int bin[25],maxpos,gg;
char ch[5005];
int main()
{
	bin[1]=1;for(int i=2;i<=20;i++)bin[i]=bin[i-1]<<1;
	scanf("%s",ch+1);int len=strlen(ch+1);
	for(maxpos=1;bin[maxpos+1]<=len;maxpos++);
	maxpos--;
	memset(ok,false,sizeof(ok));
	for(int i=0;i<bin[maxpos+1];i++)ok[0][i]=true;
	for(int i=1;i<=len-bin[maxpos+1]+1;i++)
	{
		int op='z'-'a'+1;
		for(int j=0;j<bin[maxpos+1];j++)ok[i][j]=ok[i-1][j];
		for(int j=0;j<bin[maxpos+1];j++)if(ok[i][j])op=min(op,ch[i+j]-'a'+1);
		for(int j=0;j<bin[maxpos+1];j++)if(ok[i][j]&&ch[i+j]-'a'+1!=op)ok[i][j]=false;
		for(int j=0;j<bin[maxpos+1];j++)
			for(int k=1;k<=maxpos;k++)
				if(j&bin[k])ok[i][j]|=ok[i][j^bin[k]];
		printf("%c",op+'a'-1);
	}
	puts("");
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值