Codeforces Round #739 (Div. 3) E. Polycarp and String Transformation

题目链接
在这里插入图片描述


大意:

给出一个串 s s s,每次删除其中的一种元素,将剩下的元素加到串 t t t 上,直至将所有元素删完。
现给出串 t t t,输出串 s s s 和删除顺序。

思考:

先求出删除顺序:
最后剩下的肯定是最后才删除的,那倒数第二个剩下的就是倒数第二个删除的,依此类推。。
所以从后往前枚举,先出现的为后删除的
将第一次出现的元素依次存下来,遍历完之后翻转就行了。

如何求原串s呢?
类似一种哈希的思想:
最后一定要根据一个串来求出删除后的串的,但是如果时直接枚举的话,复杂度就太大了。
于是我们先根据一些限制筛选掉很大一部分,然后就可以暴力判断是否是答案了。

将每一次删除元素后加进来的串看成一组,那整个串 t t t 就分成了若干组:第一组为原串,第二组为删除一种元素后的串,第三组为删除两个元素后的串…

最后删除的元素是存活了所有组的,倒数第二个删除的元素存活的x-1组…

所以我们可以先按照当前枚举的前缀,推算出总共有多少元素。
看其是否与所给汇合串的长度相同

  • 如果小于所给串长度,说明还没枚举到,继续枚举;
  • 如果和所给串长度相同,那当前枚举的前缀可能就是答案,暴力求出总串,判断是否相同;
  • 如果大于所给串长度,那就无解了。
    因为后面继续枚举也是以当前串为前缀,当前串推算出的总元素都大于总串了,那后面的肯定都不满足。

实现:

从前往后遍历每个位置,将从开头到该位置(前缀)看作答案。

预处理出每个元素出现的组数,遍历的时候加上当前元素出现的总组数,得到的数就是当前答案推算出的总串长度。

然后比较其与所给总串的长度大小:

  • 大于所给串长度,输出-1,无解。
  • 等于所给串长度,暴力求总串,判断是否与所给总串相同。如果是,那这就是答案。

Code:

#define mem(a,b) memset(a,b,sizeof a)
#include<iostream>
#include<cstring>
using namespace std;

const int N=500010;
int n,m,A[30];
string a,s,ans;
bool f[30]; 

bool check(string ans) //还原删除后的串 
{
	string t=ans;
	for(int k=0;k<s.size();k++)
	{
		int len=t.size();
		for(int i=0;i<ans.size();i++)
		{
			if(ans[i]=='*') continue;
			
			if(ans[i]!=s[k]) t+=ans[i];
			else ans[i]='*';
		}
	}
	if(t==a) return 1;
	return 0;
}

int main(){
	int T;cin>>T;
	getline(cin,a);
	while(T--)
	{
		getline(cin,a);
		
		s=""; //找到删除顺序s 
		mem(f,0);
		for(int i=a.size()-1;i>=0;i--)
		{
			if(!f[a[i]-'a']) s+=a[i],f[a[i]-'a']=1;
		}
		reverse(s.begin(),s.end());
		
		//A[i]表示i元素存活的回合数 
		for(int i=0;i<s.size();i++) A[s[i]-'a']=i+1;  
		
		int sum=0,flag=0;
		ans="";
		for(int i=0;i<a.size();i++) //判断以此前缀为答案时,删除后的长度是否和已给串长度相同 
		{
			sum+=A[a[i]-'a'];
			ans+=a[i];
			
			if(sum>a.size()) break; //以此前缀为答案,长度大于所给长度,无解 
			if(sum==a.size()) //长度与所给长度相等,可能为答案 
			{
				if(check(ans)){ //还原删除后的串,看是否与所给相同 
					flag=1;break;
				}
			}
		}
		
		if(!flag) cout<<-1<<"\n";
		else cout<<ans<<" "<<s<<"\n";
	}
	
	return 0;
}

纯暴力过不了,找出最可能是答案的再暴力。好思路。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值