Codeforces Round #556 (Div. 2) D - Three Religions(dp+string)

题目

一个长为n(n<=1e5)的串,q(q<=1000)次操作,

每次操作对第type(type={1,2,3})个串里添加一个字符,或删去一个字符

保证每个type串长不超过250,

问每次操作后,三个串是否能同时构成原串的子序列,且三段序列不相交

思路来源

https://www.cnblogs.com/npugen/p/10798295.html

题解

感觉是dp,然而就是不会dp,250*250*250的空间大概2e7,也是没想到吧...

先预处理Next数组,Next[i][j]表示[i,n]中第一次出现j的位置的最小下标 

这个开一个dp数组倒着处理,

如果当前第i位字母是β,更新Next[i][β],其余的字母沿用i+1的结果

然后开一个dp[i][j][k]的数组,

dp[i][j][k]代表第一个串长为i,第二个串长为j,第三个串长为k时所需的原串的最小串长

实际操作时,维护的值是所需的原串的最小下标,也就是串长减一,原理是一样的

这才有了串长为0时,下标为-1的dp[0][0][0]=-1的操作

每次更新操作时,只需更新包含更新点所在的平面片,复杂度O(q*250*250)

将dp[i][j][k]想象成三维(i,j,k)的点,模拟一下更新过程即可

删除时无需更新,下次更新时那个平面片会利用之前没被删除的结果对原来的覆盖

每次询问,只需看串长≤n也就是下标<n即可

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1e5+10;
const int maxm=255;
int n,q;
//dp[i][j][k]代表第一个串长为i,第二个串长为j,第三个串长为k时所需的原串的最小串长 
int dp[maxm][maxm][maxm];
int Next[maxn][26];//Next[i][j]表示[i,n]中第一次出现j的位置的最小下标 
int type;
char s[maxn];
char op[3],ch[3];
string now[3];//现在每个字符串的字符 
int miin[3],maax[3];
//更新下界 更新上界
//将dp[i][j][k]考虑成一个三维立体结构,则每一个值的加入只需更新一个二维平面片 
void init(char s[],int n)
{
	for(int j=0;j<26;++j)//由于串长的最大下标为n-1 
	Next[n][j]=Next[n+1][j]=n;//此处令最小下标为n,即INF
	for(int i=n-1;i>=0;--i)
	{
		int num=s[i]-'a';
		for(int j=0;j<26;++j)
		{
			if(j==num)Next[i][j]=i;//新开一个 
			else Next[i][j]=Next[i+1][j];//沿用上一个 
		}
	} 
} 
void cal(int i,int j,int k)
{
	int &ans=dp[i][j][k];
	ans=n;//先赋dp[i][j][k]=INF即n 
	//以dp[i][j][k]=min(dp[i][j][k],Next[dp[i-1][j][k]+1][now[0][i-1]-'a']为例
	//说明是上一个状态的最小下标往后的下标p里[p,n]里最早出现当前字母的位置的一个  
	if(i)ans=min(ans,Next[dp[i-1][j][k]+1][now[0][i-1]-'a']);
	if(j)ans=min(ans,Next[dp[i][j-1][k]+1][now[1][j-1]-'a']);
	if(k)ans=min(ans,Next[dp[i][j][k-1]+1][now[2][k-1]-'a']);
} 
int main()
{
	scanf("%d%d",&n,&q);
	scanf("%s",s);
	init(s,n);
	dp[0][0][0]=-1;//串长为0,则最小下标为-1 下标=串长-1 
	for(int i=1;i<=q;++i)
	{
		scanf("%s%d",op,&type);
		type--;
		if(op[0]=='+')
		{
			scanf("%s",ch);
			now[type]+=ch[0];
			for(int j=0;j<3;++j)
			{
				miin[j]=0;
				maax[j]=now[j].size();
			} 
			miin[type]=maax[type];//要更新的值 只需更新一维 
			for(int a=miin[0];a<=maax[0];++a)
			{
				for(int b=miin[1];b<=maax[1];++b)
				{
					for(int c=miin[2];c<=maax[2];++c)
					{
						cal(a,b,c);
					}
				}
			}
		}
		else now[type].pop_back(); 
		puts(dp[now[0].size()][now[1].size()][now[2].size()]<n?"YES":"NO"); 
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/89736427