题意
给定 \(n\) 个仅由小写英文字母组成的字符串,从中选出 \(k\) 个,以任意顺序排列,使得得到的字符串字典序最小。
输出字典序最小的字符串。
做法
先将所有字符串按照如下方式排序:
bool cmp(string A, string B){
return A+B<B+A;
}
然后DP。
设 \(dp[i][j]\) 表示从后 \(i\) 个字符串中选择 \(j\) 个能得到的字典序最小的字符串,转移如下:
\(dp[i][j]=min(dp[i+1][j],str[i]+dp[i+1][j-1])\)
答案即为 \(dp[1][k]\)。
妙就秒在把贪心决策提前处理了。
而且这个DP为什么是从后往前而不是从前往后,因为我们的目的是保证字典序最小,字典序更注重前面部分的串而不是后面,把后面都处理好了那么前面的大小可以决定一切。
代码
string st[maxn];
string dp[maxn][maxn];
bool cmp(string A, string B){
return A+B < B+A;
}
int main(){
n=rd(); k=rd();
for(int i(1);i<=n;++i){
cin >> st[i];
len += st[i].size();
ln[i] = st[i].size();
for(int j(0);j<ln[i];++j){
hs[i]*=26; hs[i]+=st[i][j];
}
}
sort(st+1,st+1+n,cmp);
for(int i(1);i<=n+1;++i)
dp[i][n-i+2]="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
for(int i(n);i>=1;--i){
for(int j(1);j<=n-i+1;++j){
dp[i][j]=min(dp[i+1][j],st[i]+dp[i+1][j-1]);
// printf("dp[%d][%d]=",i,j);cout<<dp[i][j]<<"\n";
}
}
cout << dp[1][k] << "\n";
}