Noip2001 提高组 T3

Posted 云深不知处

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Noip2001 提高组 T3相关的知识,希望对你有一定的参考价值。

T3

题目描述

给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<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+KMP来处理这道题

  首先需要知道题目要求是:

      不能以同一个开头生成两个单词

  千万别理解错意思、、、

  由于在拆分字符串的过程中,如果以某位置为首某个较短单词被截断,那么以该位置为首的较长单词必然也会被截断。

    也就是说,对于各个位置来说我们选取较短的单词总不会比选取较长的单词所形成的单词少。

  那么题目就转化成了在每一个位置上选取能够形成的最短的单词,如果不能形成则为0,直接continue即可

  DP转移方程也是很好想的:

      if(dp[x][j-1]) 

        dp[i][j]=max(dp[i][j],dp[x][j-1]+KMP(x+1,i));//其中x是我们枚举的匹配长度

坑点:

  注意题目要求的读入方式

上代码:

技术分享
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int L1 = 300;
const int L2 = 10;
int len;
char a[L1];
void init(int T) {
    char c;
    while(T--) {
        for(int i=0; i<20; i++) {
            scanf(" %c",&c);
            a[len++]=c;
        }
    }
}

char d[L2][L1]; //存字典
int sum,cnt;
//sum:统计一段单词中可以匹配的总个数
//cnt:字典单词总个数
int kmp[L2][L1],nowx[L2],dl[L2]; 
//nowx[]:每个单词当前具体在KMP函数中匹配的位数
//dl[]:字典中每个单词的长度 
bool vis[L1]; //判重
int KMP(int l,int r) {
    sum=0;
    memset(vis,false,sizeof(vis));
    memset(nowx,0,sizeof(nowx));
    for(int i=l; i<=r; i++) 
        for(int j=0; j<cnt; j++) {
            while(nowx[j] && a[i]!=d[j][nowx[j]])
                nowx[j]=kmp[j][nowx[j]];
            if(a[i]==d[j][nowx[j]]) nowx[j]++;
            if(nowx[j]==dl[j] && !vis[i-dl[j]+1]) {
                vis[i-dl[j]+1]=true; //标记开头 
                sum++;
            }
        }
    return sum;
} 

int dp[L1][L1];
int main() {
    int p,k,x;
    scanf("%d%d",&p,&k);
    init(p);
    scanf("%d",&cnt);
    for(int i=0; i<cnt; i++) {
        scanf("%s",d[i]);
        dl[i]=strlen(d[i]);
    }
    for(int i=0; i<cnt; i++) {
        x=0;
        kmp[i][x]=kmp[i][x+1]=0;
        for(int j=1; j<dl[i]; j++) {
            while(x && d[i][x]!=d[i][j]) x=kmp[i][x];
            kmp[i][j+1]=d[i][x] == d[i][j] ? ++x : 0;
        }
    }
    for(int i=0; i<len; i++) {
        for(int j=1; j<=k; j++) {
            if(j==1) dp[i][j]=KMP(0,i);
            else {
                for(x=0; x<i; x++)
                    if(dp[x][j-1]) 
                        dp[i][j]=max(dp[i][j],dp[x][j-1]+KMP(x+1,i));
            }
        }
    }
    printf("%d",dp[len-1][k]);
    return 0;
}
View Code

 

以上是关于Noip2001 提高组 T3的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2016提高组 Day1 T3 换教室

2012Noip提高组Day1 T3 开车旅行

2012Noip提高组Day2 T3 疫情控制

P1025 [NOIP2001 提高组] 数的划分题解

Vijos1910 NOIP2014提高组 Day2T3 解方程 其他

洛谷 P1850 换教室(NOIp2016提高组D1T3)