qoj17410 Optimal Splitting
Qoj.17410 Optimal Splitting
审题
将原串 \(S\) 拆分成两个子序列 \(X,Y\),即 \(X,Y\) 均为 \(S\) 的子序列,并且 \(S\) 的每个字符恰好属于 \(X,Y\) 中的一个串。求最后 \(T=max(X,Y),|T|=max(|X|,|Y|)\) 的字典序最小。
\(T\le 10^4 , \sum|S|\le5000\)
思路
这种构造字符串题的普遍思路,是枚举 \(T\) 当前一位能取的最小是什么,逐位取下去一定是最优的。所以考虑可行性 dp,即设 \(dp_{i,j}\) 表示是否存在 \(|X|=i,|Y|=j\) 且满足这是 \(T\) 的前缀。
容易的到 \(dp_{i,j}=(dp_{i-1,j}\wedge (S_{i+j}=T_i))\vee(dp_{i,j-1}\wedge (S_{i+j}\le T_j))\),但是发现这样时间复杂度仍旧不对。但是注意到我们可以重复利用之前的值,每次枚举最新的 \(i\) 就可以了,所以这样的时间复杂度就是 \(O(n^2|Z|)\)(\(|Z|\) 为字符集大小)。
但是这还不够,所以我们优化状态,令 \(dp_{i,j}\) 表示当前的串小于等于 \(T\) 的前缀,也就是第一个转移的条件变为 \(S{i+j}\le T_i\)。这样我们就可以二分每一位,时间复杂度变为 \(O(n^2 log|Z|)\)。这样我们就可以通过此题了。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
#define freop(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define endl '\n'
#define inf 0x7f7f7f7f
#define lb(x) ((x)&-(x))
#define pii pair<int,int>
#define fir first
#define sec second
#define mkp(a,b) make_pair(a,b)
const int N=5010;
int T,n,f[2][N],g[N],nw;
string s,p;
inline bool js(int x){
int y=(x&1),fl=0;
fl|=f[y][0]=(f[y^1][0]&&s[x-1]<=nw);
for(int i = 1;i<=x&&i+x<=n;++i)
f[y][i]=(f[y^1][i]&&s[x+i-1]<=nw),
fl|=f[y][i]|=(f[y][i-1]&&s[x+i-1]<=p[i-1]);
return fl;
}
int main(){
cin.tie(0)->ios::sync_with_stdio(false);
cout.tie(0);
// freop();
//↑以上为初始化↑
//------------------------------
cin >> T;
while(T--){
memset(f,0,sizeof(f)),
f[0][0]=1,p="",cin >> n >> s;
for(int i = 1;i<=n;++i){
int l='a',r='z'+1;p+=' ';
while(l<r){
p[i-1]=nw=(l+r>>1);
if(js(i)) r=nw,copy(f[i&1],f[i&1]+n+1,g);
else l=nw+1;
}
p[i-1]=l,copy(g,g+n+1,f[i&1]);
if(f[i&1][n-i]) break;
}
cout << p << endl;
}
return 0;
}

浙公网安备 33010602011771号