• Codeforces Round #570 (Div. 3) Subsequences (Easy and Hard Version)


    题面 : https://codeforc.es/contest/1183

    题目大意是,一个长为n(1<=n<=100)的字符串 s 的k(k在Easy Version中是 1~100,在Hard Version中是1~1e12,两种版本就此处不同)个子序列组成一个集合,得到这个集合会产生一个代价,该代价是集合中每个元素的长度与原串s的长度之差的总和,现在求这个代价最小的值,如果字符串s没有k个子序列则输出"-1"。

    初看此题,应当知道子序列不可能绝对是 2^n个,因为会有重复的子序列。

    对于 Easy Version 我们可以模拟从原串一个个生成子序列,存储子序列的集合最大为100,所以复杂度没问题。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 queue< string > que;
     5 set< string >   S;
     6 
     7 int main(){
     8     ios::sync_with_stdio(0);cin.tie(0);
     9     string str;
    10     int N,K;
    11     int ans = 0;
    12     cin >> N >> K;
    13     cin >> str;
    14     que.push(str);
    15     S.insert(str);
    16     while(!que.empty()&&S.size()<K){
    17         string cur = que.front();
    18         que.pop();
    19         int len = cur.length();
    20         for(int i = 0; i<len; ++i){
    21             string test = cur;
    22             test.erase(i,1);
    23             if(S.size()<K&&S.count(test)==0){
    24                 que.push(test);
    25                 S.insert(test);
    26                 ans += N-len+1;
    27             }
    28         }
    29     }
    30     cout << (S.size()==K?ans:-1);
    31     return 0;
    32 }
    BFS

    对于 Hard Version ,由于K可以到达1e12,显然搜索超时。

    先来看一看另外一个简单的问题,如果不考虑子序列长度,一个给定的字符串我们如何得知其所有子序列个数呢?假设从字符串str长度为len,下标从1~len,设f[i]为考虑前 i 个字符的答案,那么

    f[i] = f[i-1]*2 ;  (第 i 个字符从未出现)

    f[i] = f[i-1]*2 - f[last[str[i]]-1] ; (第 i 个字符出现过,last[]为记录该字符上一次出现的位置)

    "*2"很显然表示的是添加这个字符或不加,然而有重复的子序列在其中,需要减去这个字符上一次作为结尾的序列,"-1"便是该字符上一个位置之前的所有子序列并加上它结尾,没有"-1”则多减去不以它结尾的子序列。

    好的,了解这个经典的问题后,我们再来解决这个 hard version ,现在我们给dp的状态额外加一个维度表示子序列长度,f[i][j] 即是考虑前 i 个字符,长为 j 的子序列。

    f[i][j] = f[i-1][j-1] + f[i-1][j] ; (第 i 个字符从未出现)

    f[i][j] = f[i-1][j-1] + f[i-1][j] - f[last[str[i]]-1][j-1] ; (第 i 个字符出现过,last[]为记录该字符上一次出现的位置)

    答案就从 f[len][len ~ 0] 里找前 K 个进行计算,BTW为了防止数字过大溢出,在转移的时候加一个 min (K , f[i][j]);  这样可防止溢出并保证答案正确 。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 typedef long long ll;
     5 
     6 ll f[123][123];
     7 int last[26];
     8 int main(){
     9     ios::sync_with_stdio(0);
    10     int N;
    11     ll K;
    12     cin >> N >> K;
    13     string str;
    14     cin >> str;
    15     f[0][0] = 1;
    16     for(int i=1;i<=N;++i){
    17         f[i][0] = 1;
    18         for(int j=1;j<=i;++j){
    19             if(last[str[i-1]-'a'])
    20                 f[i][j] = min(K , f[i-1][j-1] + f[i-1][j]
    21                         - f[last[str[i-1]-'a']-1][j-1] );
    22             else
    23                 f[i][j] = min(K , f[i-1][j-1] + f[i-1][j]);
    24         }
    25         last[str[i-1]-'a'] = i;
    26     }
    27     ll ans = 0;
    28     for(int i=N;i>=0;i--){
    29         ll cur = min(f[N][i],K);
    30         //cout << "f["<< N <<"]["<<i<<"]"<<f[N][i] << endl;
    31         ans += cur * (N-i);
    32         K -= cur;
    33 
    34     }
    35     cout << (K==0?ans:-1);
    36     return 0;
    37 }
    dpNN

    另外还有一个dp的方法,状态用 dp[i][ch] 表示长度为 i 以字符 ch 结尾的子序列个数。有空再写。

  • 相关阅读:
    SignalR2结合ujtopo实现拓扑图动态变化
    SignalR2简易数据看板演示
    使用SignalR 2进行服务器广播
    使用SignalR实时Web应用程序
    ZooKeeper安装
    MongoDB安装
    线程安全与非线程安全
    监听器,事件对象,事件源
    Graphics与Canvas
    JDialog
  • 原文地址:https://www.cnblogs.com/Kiritsugu/p/11105992.html
Copyright © 2020-2023  润新知