您的位置:首页 > 其它

洛谷P1026 统计单词个数

2016-02-15 16:32 197 查看
 

题意: 给出一个长度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 1007

using 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++); //match
if(j == dictLen[d]) //match success
minFit = 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: