题目链接:http://codeforces.com/contest/1183/problem/E
题意:现在有一个字符串长度为 n n n,你需要从字符串中找 k k k个不同的子序列出来,每个子序列的代价为字符串长度减去子序列长度,问怎样使总代价和最小,如果找不到 k k k个直接输出 − 1 -1 −1。
解题心得:因为n和k都比较小,可以直接 B F S BFS BFS搜索,每次搜索从当前串枚举删去一个字符,产生新的字符串然后压入队列,但是需要判断重合,可以直接用字典树标记查重。
#include <bits/stdc++.h>
using namespace std;
typedef complex<double> cp;
typedef long long ll;
const int maxn = 2e5+100;
const double pi = acos(-1);
int node[maxn][30], pos = 0, n ,k;//node是记录字典树的结点,pos是按照顺序顺序安排字典树的节点
bool End[maxn];//记录当前节点是否是一个字符串的结束点
string s;
void Mark(string st) {//字典树标记
int root = 0;
for(int i=0;i<st.size();i++) {
if(node[root][st[i]] == 0) {
node[root][st[i]] = ++pos;
root = pos;
} else {
root = node[root][st[i]];
}
if(i == st.size()-1) {
End[root] = true;
}
}
}
bool checke(string st) {//字典树查重
int root = 0;
for(int i=0;i<st.length();i++) {
if(node[root][st[i]] == 0) return true;
root = node[root][st[i]];
if(i == st.length()-1) {
if(End[root]) return false;
}
}
return true;
}
bool Empty;
void BFS() {
int ans = 0;
queue <string> qu;
qu.push(s);
Mark(s);
while(!qu.empty()) {
string now = qu.front(); qu.pop();
ans += (n - now.length());
k--;//每次搜索减一
if(k == 0) {
printf("%d\n", ans);
return ;
}
for(int i=0;i<now.length();i++) {
string temp2 = now;
temp2.erase(temp2.begin()+i);
if(Empty && temp2.length() == 0) continue;
if(checke(temp2)) {
if(temp2.length() == 0) Empty = true;
Mark(temp2);
qu.push(temp2);
}
}
}
puts("-1");//找不到到k个
}
int main() {
// freopen("1.in.txt", "r", stdin);
scanf("%d%d",&n, &k);
cin>>s;
BFS();
return 0;
}