显然肯定有一种方案使删的每一段子串在原字符串中是互不包含的,因为包含和相邻是等价的。
那么有一个简单的DP:
设
fi,j
f
i
,
j
表示删完后还有
i
i
位,已删除的长度为 时的最优答案。
由于
fi,j
f
i
,
j
是个字符串,这样复杂度是
O(n3logn)
O
(
n
3
log
n
)
的。
然后考虑能否不记字符串。注意到每个状态都是答案的一个前缀,那么对于
fi,j
f
i
,
j
和
fi,k
f
i
,
k
,若
fi,j<fi,k
f
i
,
j
<
f
i
,
k
,那么
fi,k
f
i
,
k
就没有作用了。所以只需要记下最小的那个
fi,j
f
i
,
j
就可以了。
将
fi,j
f
i
,
j
改成布尔型,表示是否是最小字符串。枚举
i
i
,每次从最小字符串中选一个下一位最小的作为第 位的字符,然后把大于它的都改成
false
f
a
l
s
e
就好了。
时间复杂度
O(n2logn)
O
(
n
2
log
n
)
。
#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int k,n,m;
int f[N],F[2][N];
char s[N];
bool b;
int main(){
scanf("%s",s);
n=m=strlen(s);
f[0]=1;
while((1<<k)<=n)k++;k--;
m-=(1<<k)-1;
for(int i=0;i<m;i++){
memcpy(F[b],f,sizeof(f));
for(int j=0;j<k;j++,b^=1){
memcpy(F[b^1],F[b],sizeof(f));
for(int p=0;p<(1<<k);p++)F[b^1][p|(1<<j)]|=F[b][p];
}
memcpy(f,F[b],sizeof(f));
char Ans='z';
for(int j=0;j<(1<<k);j++){
int pos=i+j;
if(!f[j]||pos>=n)continue;
Ans=min(Ans,s[pos]);
}
putchar(Ans);
for(int j=0;j<(1<<k);j++){
int pos=i+j;
if(!f[j]||pos>=n||s[pos]!=Ans)f[j]=0;
}
}
return 0;
}