洛谷P1026 统计单词个数

来源:互联网 发布:java的compare方法 编辑:程序博客网 时间:2024/06/07 22:42

 

题意: 给出一个长度L <= 200的由小写英文字母组成的字母串, 要求将此字母串分成K份(1 < K <= 40), 且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠, 但不能共用首字母). 单词数量 <= 6. 要求输出这个最大的总数.

 

         这题说的是对于每个"份"的单词数量求和, 但这样想似乎比较麻烦. 其实, 可以把"分成K份"转化为"切K-1刀", 然后尽量让这K-1刀破坏的单词数量少即可. 方便起见, 定义"空位i" 为第i-1个字母后面, 第i个字母前面的"空位". 在空位处是可以下刀的. 空位编号是1 ~到 L-1.

         注意到单词可以重叠但不能共首字母, 所以其实是关心有多少个首字母可以匹配到单词. 又注意到在同一首字母下长的匹配严格不如短的匹配, 因此首先想求一个数组minFit, 保存每个位置的最短匹配长度. 这样, 对于位置i, 设n = minFit[i], 则i+1, i+2, ..., i+n-1这些空位处下刀都会破坏位置i的匹配, 从而导致单词总数-1. 所以求出各位置的最短匹配之后, 就可以维护各个空位的"破坏数" cut[i], 也就是切断空位i破坏多少单词.

         但是这样之后, 选择破坏数最小的K个空位就可以了么? 并不, 因为空位之间是相互影响的,可能切断前面一个空位会导致后面的空位的破坏数减小. 比如有单词abcd, 然后字符串里有子串xxxabcdxxx, 则ab, bc, cd之间破坏数都是1, 然而任意切断一个之后, 另两个的破坏数就减成0了. 因此cut[i]这个数组信息不足, 应该存下前一刀的位置, 把数组改成cut[i][j], 表示前一刀在空位i, 这一刀在空位j的破坏数. (默认从前往后切) 这样就可以考虑空位之间的相互影响. 维护的方法就是, 对于位置i, 仍设n = minFit[i], 则对于j,k满足j <= i, i+1 <= k <= i+n-1, 此时前一刀在空位j, 后一刀在空位k会破坏位置i的匹配, 因此对于所有满足条件的j,k都要cut[j][k]++.

         然后如何确定最小总破坏呢?这大致可以看到DP的结构, 从前往后递推, 根据下刀位置和这是第几刀就能够确定最小破坏. dp[i][j]表示在位置i下第j刀, 造成的最小的总破坏. 则递推公式为:

         dp[i][j]= min{dp[k][j-1] + cut[k][i]}, 1 <= k < i

         还要注意一下边界条件, 默认必须在空位0下第0刀, 因此dp[0][0] =0, 其他的dp[x][0]都设为INF. 其实还有一点是, 在位置i下第j刀的话, 必须有i >= j. (位置i与i之前至多能下i刀), 不满足的都应该设为INF. 但是只要维护好dp[x][0]的边界条件, 这个是自动成立的.

         最后在dp[x][K-1]里面找最小值, 就是整个字符串切K-1刀最小的损失, 然后用匹配长度 >0的位置总数(也就是不切的时候的单词匹配总数)减掉最小损失即得最大单词总数.


#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#define INF 1007using namespace std;char dict[7][207];int dictLen[7];char str[207];int cut[207][207];int dp[207][47];int main(){int P,K,S;while(cin >>P >>K){memset(str, 0, sizeof(str));memset(cut, 0, sizeof(cut));memset(dp, 0, sizeof(dp));for(int i = 0; i < P; i++)cin >>str + i*20;cin >>S;for(int i = 0; i < S; i++){cin >>dict[i];dictLen[i] = strlen(dict[i]);}int len = P*20;int totalFit = 0;for(int i = 0; i < len; i++)//each position{int minFit = INF;for(int d = 0; d < S; d++)//each word{int j;for(j = 0; j < dictLen[d] && str[i+j] == dict[d][j]; j++);//matchif(j == dictLen[d])//match successminFit = min(minFit, j);}if(minFit < INF){totalFit++;for(int j = 0; j <= i; j++)for(int k = i + 1; k < i + minFit; k++)cut[j][k]++;}}/*dp[0][0] = 0;for(int i = 1; i < len; i++)dp[i][0] = INF;for(int i = 0; i <= K; i++)for(int j = i+1; j <= K; j++)dp[i][j] = INF;*/for(int i = 0; i < len; i++)for(int j = 0; j < K; j++)dp[i][j] = INF;dp[0][0] = 0;for(int p = 1; p < len; p++)//position{dp[p][1] = dp[0][0] + cut[0][p];for(int c = 2; c <= p && c < K; c++){for(int l = 1; l < p; l++)dp[p][c] = min(dp[p][c], dp[l][c-1] + cut[l][p]);}}int minLost = INF;for(int p = K-1; p < len; p++)minLost = min(minLost, dp[p][K-1]);cout <<totalFit - minLost <<endl;}return 0;}


0 0