题目链接
(Gym) https://codeforces.com/gym/101239
(BZOJ) 大人,时代变了。
题解
这题好神仙啊
首先有一个显然的 DP,按长度从小到大排序,维护一下目前可选的方案中除了 (i) 所在的组之外的组的最后一个元素都有哪些可能性。在 (O(L)) 时间内判断一个串是否是另一个的子序列,时间复杂度 (O(n^2L)).
但是这样并不能过,有一个很神仙的结论是,任何一个时刻有用的可选的方案只有至多 (2) 种,特别地,如果有 (2) 种,设为 ((i-1,x)) 和 ((i-1,y)) 则一定满足 (x) 和 (y) 都是 ((i-1)) 的子序列。
这可以用数学归纳法证明。最一开始显然只有一种可选方案。
如果某一时刻只有一种可选方案(不妨设为 ((i-1,x))),那么下一个时刻只有可能出现 ((i,x)) 和 ((i,i-1)) 这两种。如果两种都出现,那么显然满足 ((i-1)) 和 (x) 都是 (i) 的子序列。
如果某一时刻有两种可选方案且满足上述条件(不妨设为 ((i-1,x)) 和 ((i-1,y))):
如果 ((i-1)) 是 (i) 的子序列,那么 ((i,x)) 和 ((i,y)) 都会成为候选集合,且 (x) 和 (y) 都是 (i) 的子序列。这时 ((i,i-1)) 还有可能成为候选集合,但是我们注意到如果 ((i,i-1)) 成为了候选集合,那么因为 (x) 和 (y) 都是 ((i-1)) 的子序列,即 ((i,i-1)) 可以被 ((i,x)) 和 ((i,y)) 完全替代!所以我们可以舍弃 ((i,i-1)) 这种可能性,可选集合最多还是只有 (2) 个。
否则,可选集合只可能有 ((i,i-1)) 这一种。
所以转移就可以了,时间复杂度 (O(nL)).
代码
#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
#define pii pair<int,int>
using namespace std;
inline int read()
{
int x = 0,f = 1; char ch = getchar();
for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
return x*f;
}
const int mxN = 4000;
int n;
string a[mxN+3];
vector<pii> s[mxN+3];
vector<int> ans[2];
bool cmp_len(string s,string t) {return s.length()<t.length();}
bool judge(int u,int v)
{
for(int i=0,j=0; i<a[u].length(); i++)
{
while(j<a[v].length()&&a[v][j]!=a[u][i]) {j++;}
if(j==a[v].length()) {return false;} j++;
}
return true;
}
int main()
{
n = read(); cin>>a[n+1];
for(int i=1; i<=n; i++) cin>>a[i];
sort(a+1,a+n+1,cmp_len);
for(int i=1; i<=n; i++) if(!judge(i,n+1)) {puts("impossible"); return 0;}
s[0].push_back(mkpr(0,0));
for(int i=1; i<=n; i++)
{
if(s[i-1].size()==1)
{
if(judge(i-1,i)) {s[i].push_back(mkpr(s[i-1][0].x,i-1));}
if(i-1!=s[i-1][0].x&&judge(s[i-1][0].x,i)) {s[i].push_back(mkpr(i-1,s[i-1][0].x));}
}
else if(s[i-1].size()==2)
{
if(judge(i-1,i)) {s[i].push_back(mkpr(s[i-1][0].x,i-1)); s[i].push_back(mkpr(s[i-1][1].x,i-1));}
else if(judge(s[i-1][0].x,i)) {s[i].push_back(mkpr(i-1,s[i-1][0].x));}
else if(judge(s[i-1][1].x,i)) {s[i].push_back(mkpr(i-1,s[i-1][1].x));}
}
if(!s[i].size()) {puts("impossible"); return 0;}
}
for(int i=n,j=s[n][0].x,x=0; i>=1; i--)
{
ans[x].push_back(i);
for(int k=0; k<s[i].size(); k++) if(s[i][k].x==j)
{
if(s[i][k].x==i-1) {x^=1; j = s[i][k].y;}
else {j = s[i][k].x;}
break;
}
}
printf("%d %d
",ans[0].size(),ans[1].size());
for(int i=ans[0].size(); i>=1; i--) cout<<a[ans[0][i-1]]<<endl;
for(int i=ans[1].size(); i>=1; i--) cout<<a[ans[1][i-1]]<<endl;
return 0;
}