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 nxt[L2][L1],nowx[L2],dl[L2]; 
//nxt[]:开两维的原因是因为----不止一个模式串要跟文本串进行匹配 
//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]=nxt[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;
        nxt[i][x]=nxt[i][x+1]=0;
        for(int j=1; j<dl[i]; j++) {
            while(x && d[i][x]!=d[i][j]) x=nxt[i][x];
            nxt[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

如果运气好也是错,那我倒愿意错上加错!

❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/7749696.html