Luogu 统计单词个数

来源:互联网 发布:jre1.8 源码下载 编辑:程序博客网 时间:2024/05/18 01:53

题目描述

给出一个长度不超过200的由小写英文字母组成的字母串(该字串以每行20个字母的方式输入,且保证每行一定为20个)。
要求将此字母串分成k份(2<=k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠)。
当选用一个单词之后,其第一个字母不能再用。例如字符串 this 中可包含 this 和 is,选用 this 之后就不能包含 th。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。

输入输出格式

输入格式:
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。

输出格式:
一个整数,分别对应每组测试数据的相应结果。

输入输出样例

输入样例#1:
1 3
thisisabookyouareaoh
4
is
a
ok
sab

输出样例#1:
7

说明
this/isabookyoua/reaoh


这道题想了很久,最终想出来了一个非搜索非DP的做法,而且速度还很快。

❀ 先将字典按单词长度从小到大排序,因为如果两个单词的起始位置一样,优先选择长度短的单词。
❀ 用结构体 a 来记录每个位置被多少个单词覆盖,a[i].v 表示字串里 i 这个位置被 v 个单词覆盖。
❀ 忽略 k 直接从字串里查一共有几个单词,记总数为 tot,找到一个单词就把这个单词覆盖位置的 v++。
❀ 注意每个单词最后一个字母的 v 值不需要累加,因为截断 i 这个位置指的是在字母 i 后面截断,而在最后一个字母后面截断对整个单词无影响。
❀ 将 a 按 v 值由小到大进行排序,这样被单词覆盖少的位置就排到了前面,如果要截断的话,优先截断这些位置;由于顺序会被打乱,所以在排序前用 a[i].t 记录原来的位置。
❀ 这样就可以直接从头开始用 tot 减去 k 个 a[i].v 了吗?还需要注意一点的是,截断一个位置后,覆盖这个位置的单词消失,因此要把这个消失的单词覆盖的位置都减一,防止后面重复减。

思路是正确的,程序应该还有一些不完善的地方,因为本人比较懒啦,少写了一些条件,不过我不想改它了,至少它在各大OJ上AC了。
我自己就可以找出一些反例让它爆零。所以不要copy我的程序,按着上面的思路来做吧。

下面是一个假的程序(◑▽◐)

//统计单词个数#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,k,p,tot,l;struct tWord{    char s[210];    int left[210],l,n;}d[10];struct tWide{    int v,t;}a[210];char st[210];bool cmp(tWord x,tWord y){    return x.l<y.l;}bool cmp2(tWide x,tWide y){    return x.v<y.v;}void start(){    char ch[210]={};    bool head[210]={};    char *h;    for(int i=0;i<p;++i)    {        strcpy(ch,st);        h=strstr(ch,d[i].s);        while(h!=NULL)        {            if(head[h-ch]==0)            {                ++tot;                d[i].left[d[i].n++]=h-ch;                head[h-ch]=1;                for(int j=h-ch;j<h-ch+d[i].l-1;++j)                    ++a[j].v;            }            ch[h-ch]='*';            h=strstr(ch,d[i].s);        }    }}void cut(){    for(int i=0;i<l;++i) a[i].t=i;    a[0].v=0x7fffffff;    sort(a,a+l,cmp2);    for(int i=0;i<k;++i)    {        tot-=a[i].v;        for(int j=0;j<p;++j)            for(int k=0;k<d[j].n;++k)                if(d[j].left[k]==a[i].t)                    for(int m=a[i].t;m<a[i].t+d[j].l;++m)                        --a[m].v;    }}int main(){    scanf("%d%d",&n,&k);    --k;    for(int i=0;i<n;++i)    {        char ch[25];        scanf("%s",ch);        strcat(st,ch);        l+=20;    }    scanf("%d",&p);    for(int i=0;i<p;++i)    {        scanf("%s",d[i].s);        d[i].l=strlen(d[i].s);    }    sort(d,d+p,cmp);    start();    cut();    printf("%d\n",tot);    return 0;}