洛谷 P1026统计单词个数题解--zhengjun

题目描述

给出一个长度不超过 \(200\) 的由小写英文字母组成的字母串(该字串以每行 \(20\) 个字母的方式输入,且保证每行一定为 \(20\) 个)。要求将此字母串分成 \(k\) 份,且每份中包含的单词个数加起来总数最大。

每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this 中可包含 thisis,选用 this 之后就不能包含 th

单词在给出的一个不超过 \(6\) 个单词的字典中。

要求输出最大的个数。

输入格式

每组的第一行有两个正整数 \(p,k\)\(p\) 表示字串的行数,\(k\) 表示分为 \(k\) 个部分。

接下来的 \(p\) 行,每行均有 \(20\) 个字符。

再接下来有一个正整数 \(s\),表示字典中单词个数。 接下来的 \(s\) 行,每行均有一个单词。

输出格式

\(1\)个整数,分别对应每组测试数据的相应结果。

输入输出样例

输入 #1 复制
1 3
thisisabookyouareaoh
4
is
a
ok
sab
输出 #1 复制
7

说明/提示

【数据范围】

对于 \(100\%\) 的数据,\(2 \le k \le 40\)\(1 \le s \le 6\)

【样例解释】

划分方案为 \(this / isabookyoua / reaoh\)

思路

首先,这是一道有点难度的动态规划。
\(f_{i,j}\) 表示 从尾巴用了 \(i\) 个字符分了 \(j\) 段的最多有多少个单词。
则转移方程不难想到:
\(f_{i,j}=max(f_{i,j},f_{l,j-1}+sum_{l+1,i})\)
\(sum\) 就是从 \(j\)\(i\) 的单词个数
然后 \(sum\) 就可以在前面预处理暴力算出来

代码

#include<bits/stdc++.h>
using namespace std;
int p,n,m,k,f[210][50],sum[210][210];
string s,a[10];
bool check(int l,int r){
    string x=s.substr(l,r-l+1);//这个函数就是返回字符串s从第l个字符开始后面的r-l+1个字符所组成的字符串
    for(int i=1;i<=n;i++)
	     if(x.find(a[i])==0)//这个函数可以返回a[i]在x中最早出现的位置
		     return 1;
    return 0;
}
int main(){
    string ch;
    s+='0';
    cin>>p>>k;
    for(int i=1;i<=p;i++){
        cin>>ch;
        s+=ch;
    }
    cin>>n;
	m=s.length()-1;
    for(int i=1;i<=n;i++)
		cin>>a[i];
    for(int i=m;i>=1;i--)
    	for(int j=i;j>=1;j--){
    	    sum[j][i]=sum[j+1][i];
   	        if(check(j,i))
			    sum[j][i]++;
   		}
    f[0][0]=0;
    for(int i=1;i<=k;i++)
		f[i][i]=f[i-1][i-1]+sum[i][i];
    for(int i=1;i<=m;i++)
		f[i][1]=sum[1][i];
    for(int i=1;i<=m;i++)
    	for(int j=1;j<=k&&j<i;j++)
    		for(int k=j;k<i;k++)
    			f[i][j]=max(f[i][j],f[k][j-1]+sum[k+1][i]);
    printf("%d",f[m][k]);
    return 0;
}

谢谢--zhengjun

posted @ 2022-06-10 18:59  A_zjzj  阅读(32)  评论(0编辑  收藏  举报