题目链接
题意
在本题中,子串是可以通过删去原串若干个字符得到的字符串。特别的,空串也算子序列。
给定一个长度为 n 的字符串 s,选出 k 个本质不同的子串,最小化代价。
选择一个长度为 m 的子串的代价为 n−m,即删去的字符数量。
思路
考虑 DP 求解。
设 fi,j 为前 i 个字符中长度为 j 的本质不同的子串数量。
容易想到 fi,j=fi−1,j+fi−1,j−1。
但这样可能选出重复子串。
比如字符串 aab
,在 f3,2 时子串 ab
会被算两次。
考虑容斥,记 lasc 为字符 c 上次出现时的所在位置。
fi,j 还要减去由 lass(i) 结尾的所有子串数量,即 flass(i)−1,j−1。
推出转移方程:
fi,j={fi−1,j+fi−1,j−1fi−1,j+fi−1,j−1−flass(i)−1,j−1lassi=0otherwise
最后求答案时,尽量选长度长的子串。
实现
空串也算子串,所以 fi,0=1。
若 k>∑i=0nfn,i,输出 -1
即可。
时间复杂度为 O(n2),其实数据可加强(你在想什么)。
代码
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
| #include<cstdio> using namespace std; const int N=100; int n,las['z'+1]; long long k,f[N+1][N+1],ans; char s[N+2]; template<class T>inline T min(T x,T y) { return x<=y?x:y; } int main() { scanf("%d%I64d",&n,&k); scanf("%s",s+1); f[0][0]=1; for(int i=1;i<=n;++i) { f[i][0]=1; for(int j=1;j<=n;++j) { f[i][j]=f[i-1][j]+f[i-1][j-1]; if(las[s[i]]) { f[i][j]-=f[las[s[i]]-1][j-1]; } } las[s[i]]=i; } for(int i=n;(~i)&&k;--i) { long long tem=min(k,f[n][i]); k-=tem,ans+=tem*(n-i); } k?puts("-1"):printf("%I64d\n",ans); return 0; }
|