对一些性质的观察。
考虑朴素的 DP,先将 $\frac{n(n-1)}{2}$ 个字串排序,然后设 $f(i,j)$ 表示对于前缀 $i$,最后一个选择的是 $s[j,i]$,转移类似于二位偏序,可以使用树状数组优化至 $\mathcal O(n^2\log n)$。
注意到一个性质,若最优方案中存在相邻两个串 $s_i,s_{i+1}$ 有 $|s_{i+1}|-|s_i|>1$,那么显然可以将 $s_{i+1}$ 的长度调整至 $|s_i|+1$。那么所有答案串最长长度满足 $\frac{len(len+1)}{2}\le n$,即 $len\le \sqrt{2n}$,我们只用保留这样的串就可以了。最终时间复杂度为 $\mathcal O(n\sqrt{n}\log n)$。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<vector> #define lowbit(x) x&(-x) #define fi first #define se second using namespace std; using PII=pair<int,int>; const int N=2.5e4+10,B=320+10; int n,lim; char s[N]; int tot=1,tr[N*B][2]; int ans; vector<int>pos[N*B]; vector<PII>op; struct BIT { int t[N]; void add(int x,int k){for(;x<=n;x+=lowbit(x))t[x]=max(t[x],k);} int ask(int x){int res=0;for(;x;x-=lowbit(x))res=max(res,t[x]);return res;} }t; void insert(int x) { int u=1,m=min(n,x+lim-1); for(int i=x;i<=m;i++) { int &v=tr[u][s[i]-'0']; if(!v)v=++tot; pos[u=v].push_back(i); } } void dfs(int x,int len) { op.clear(); for(auto i:pos[x])op.push_back({i,t.ask(i-len)+1}); for(auto x:op)t.add(x.fi,x.se); for(int i=0;i<2;i++)if(tr[x][i])dfs(tr[x][i],len+1); } int main() { scanf("%d%s",&n,s+1); lim=sqrt(n<<1); for(int i=1;i<=n;i++)insert(i); dfs(1,0); printf("%d\n",t.ask(n)); return 0; }
|