【洛谷6908】[ICPC2015 WF] Evolution in Parallel(贪心)
- 给定一个字符串\(S\)和\(n\)个字符串\(s_i\),首先要求判断是否所有\(s_i\)都是\(S\)的子序列。
- 然后要求把这\(n\)个字符串分成两组,满足同组中的所有字符串都是子序列关系。
- \(n,|S|,|s_i|\le4000\)
贪心
先将字符串按照长度排个序,然后从短到长考虑每个字符串,维护好两个字符串集合\(A,B\)。
如果当前字符串无法加入任何集合显然无解。
如果当前字符串只能加入一个集合就直接加入。
否则,我们另外维护一个集合\(C\),先判断当前字符串能否加入集合\(C\):能加入就加入,否则随便把集合\(C\)加到某个集合中,把当前字符串加入另外一个集合。(由于每个集合只需要考虑最后一个字符串,因此这里集合的选择没有影响)
代码:\(O(n|s|)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 4000
using namespace std;
int n,A[N+5],B[N+5],C[N+5];string s[N+5];
I bool cmp(Con string& x,Con string& y) {return x.length()<y.length();}
I bool Check(CI x,CI y)//判断x是否为y的子序列
{
RI i,j,n=s[x].length(),m=s[y].length();
for(i=j=0;i^n;++i,++j) {W(j^m&&s[x][i]^s[y][j]) ++j;if(j==m) return 0;}return 1;
}
int main()
{
RI i,j;for(scanf("%d",&n),cin>>s[n+1],i=1;i<=n;++i) cin>>s[i];
RI a=0,b=0,c=0,p,q;for(sort(s+1,s+n+1,cmp),i=1;i<=n;++i)//按字符串从短到长考虑
{
if(p=Check(A[a],i),q=Check(B[b],i),!p&&!q) return puts("impossible"),0;//如果两个集合都无法加入
if(p&&q) {if(Check(C[c],i)) C[++c]=i;else {for(j=1;j<=c;++j) A[++a]=C[j];c=0,B[++b]=i;}continue;}//如果两个集合都能加入,判断能否加入集合C
if(p) {if(Check(C[c],i)) for(j=1;j<=c;++j) A[++a]=C[j];else for(j=1;j<=c;++j) B[++b]=C[j];c=0,A[++a]=i;}//只能加入集合A,判断C的归宿
else {if(Check(C[c],i)) for(j=1;j<=c;++j) B[++b]=C[j];else for(j=1;j<=c;++j) A[++a]=C[j];c=0,B[++b]=i;}//只能加入集合B,判断C的归宿
}
for(i=1;i<=c;++i) A[++a]=C[i];if(!Check(A[a],n+1)||!Check(B[b],n+1)) return puts("impossible"),0;//清空集合C,检验是否为S的子序列
for(printf("%d %d\n",a,b),i=1;i<=a;++i) cout<<s[A[i]]<<endl;for(i=1;i<=b;++i) cout<<s[B[i]]<<endl;return 0;//输出答案
}
待到再迷茫时回头望,所有脚印会发出光芒