CF 1560E E. Polycarp and String Transformation(思维)
https://codeforces.com/contest/1560/problem/E
题意:
有一个串s和一个空串t,可以进行若干次以下操作,
每次操作先把s拼在t的后面,然后删掉s中的一种字母
直至s为空串
现在给出t,还原出s以及删除字母的顺序
原始思路:
t串中最后一个字母就是最后删除的字母,再往前碰到的就是倒数第二次删除的字母,再往前就是倒数第三次删除的字母,……,这样可以还原删除字母的顺序
假设字母一共有x种,最后一个删除的字母被拼接了x次,所以用这种字母在t串中的总次数除以x,就得到最后一次拼接的长度;以此类推往前,能够还原每次被拼接的串
每次还原都有限制不可以出现的字母,如果他们出现了就无解
但是只根据长度可能会导致顺序不符合,所以还原出的s还要按规则产生一个t,与给定的t相等才可以
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h>
using namespace std;
#define N 500004
char t[N];
char s[N],a[N],ss[N];
int sum[27][N];
bool vis[27];
int main()
{
int T,n,all,need,last,div,tmp,len,L;
bool tag,ok;
scanf("%d",&T);
while(T--)
{
scanf("%s",t+1);
n=strlen(t+1);
reverse(t+1,t+n+1);
for(int i=1;i<=26;++i)
{
sum[i][0]=0;
vis[i]=false;
}
all=0;
for(int i=1;i<=n;++i)
{
if(!vis[t[i]-'a'+1])
{
a[++all]=t[i];
vis[t[i]-'a'+1]=true;
}
for(int j=1;j<=26;++j) sum[j][i]=sum[j][i-1];
sum[t[i]-'a'+1][i]++;
}
div=all;
last=0;
need=1;
for(int i=1;i<=26;++i) vis[i]=false;
vis[a[1]-'a'+1]=true;
ok=true;
for(int i=1;i<=n && need<all;++i)
{
tag=true;
if(!vis[t[i]-'a'+1])
{
ok=false;
break;
}
for(int j=1;j<=need && tag;++j)
{
tmp=sum[a[j]-'a'+1][i]-sum[a[j]-'a'+1][last];
if(tmp*(all-j+1)!=sum[a[j]-'a'+1][n]) tag=false;
}
if(tag)
{
++need;
vis[a[need]-'a'+1]=true;
last=i;
div--;
}
}
if(need!=all) ok=false;
else
{
reverse(a+1,a+all+1);
len=L=0;
for(int i=n;i>last;--i) s[++len]=t[i];
reverse(t+1,t+n+1);
for(int i=1;i<=26;++i) vis[i]=true;
for(int i=1;i<=all;++i)
{
for(int j=1;j<=len;++j)
if(vis[s[j]-'a'+1]) ss[++L]=s[j];
vis[a[i]-'a'+1]=false;
}
if(L!=n) ok=false;
for(int i=1;i<=L && ok;++i)
if(ss[i]!=t[i]) ok=false;
}
if(!ok) printf("-1\n");
else
{
for(int i=1;i<=len;++i) printf("%c",s[i]);
printf(" ");
for(int i=1;i<=all;++i) printf("%c",a[i]);
printf("\n");
}
}
}
做麻烦了
t串中,以每个字母的最后出现位置从小到大排序,排序结果就是字母的删除顺序
然后就可以知道每个字母被拼接多少次,根据出现总次数判断拼接次数是否合法,同时得到s串长度
最后用s按规则产生一个t,判断与给定的t是否一样
不需要还原每次被拼接的串