题目链接

(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\n",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;
}