题目描述
给出一个长度不超过 200200 的由小写英文字母组成的字母串(该字串以每行 2020 个字母的方式输入,且保证每行一定为 2020 个)。要求将此字母串分成 kk 份,且每份中包含的单词个数加起来总数最大。
每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this
中可包含 this
和 is
,选用 this
之后就不能包含 th
。
单词在给出的一个不超过 66 个单词的字典中。
要求输出最大的个数。
输入格式
每组的第一行有两个正整数 p,kp,k。 pp 表示字串的行数,kk 表示分为 kk 个部分。
接下来的 pp 行,每行均有 2020 个字符。
再接下来有一个正整数 ss,表示字典中单词个数。 接下来的 ss 行,每行均有一个单词。
输出格式
输入输出样例
![](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
思路
题意有个地方说的比较模糊,就是重叠的情况下的影响。
实际上是用了this可以再选is,但是不能再选第一个字母t,也就是选满了main_len之后就不能选了。
大概懂了之后就知道这是一个递推找字符串的子串里满足能在字典中找到单词且能出现几个单词。
用 sum [ i ] [ j ] 来表示主字符串里 i ~ j 区间内有几个单词。
用 f [ i ] [ j ] 来表示到第 i 个字符切成 j 段能获得的最大单词数。
显然可以如下转移:
f [ i ] [ k ] = max ( f [ i ] [ k ] , f [ j ] [ k - 1] + sum [ i - 1 ] [ j ] ) ; 其中 k ∈ [ 2, min ( k, j + 1 ) ] ;
CODE
#include <bits/stdc++.h>
#define dbg(x) cout << #x << "=" << x << endl
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
template<class T>inline void read(T &res)
{
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
namespace _buff {
const size_t BUFF = 1 << 19;
char ibuf[BUFF], *ib = ibuf, *ie = ibuf;
char getc() {
if (ib == ie) {
ib = ibuf;
ie = ibuf + fread(ibuf, 1, BUFF, stdin);
}
return ib == ie ? -1 : *ib++;
}
}
int qread() {
using namespace _buff;
int ret = 0;
bool pos = true;
char c = getc();
for (; (c < '0' || c > '9') && c != '-'; c = getc()) {
assert(~c);
}
if (c == '-') {
pos = false;
c = getc();
}
for (; c >= '0' && c <= '9'; c = getc()) {
ret = (ret << 3) + (ret << 1) + (c ^ 48);
}
return pos ? ret : -ret;
}
int p, k;
string a, b;
string ss[15];
int sum[1000][1000];
int f[1000][1000];
int main()
{
read(p);read(k);
for ( int i = 1; i <= p; ++i ) {
cin >> b;
a += b;
}
int main_len = a.size();
int n;
read(n);
vector<int> len(n + 7);
for ( int i = 1; i <= n; ++i ) {
cin >> ss[i];
len[i] = ss[i].size();
}
for ( int i = 0; i < main_len; ++i ) {
for ( int j = i; j < main_len; ++j ) {//todo:枚举子段
for ( int k = i; k <= j; ++k ) {
int ok = 1;
for ( int z = 1; z <= n; ++z ) {
if(k + len[z] - 1 > j)
continue;
for ( int w = 0; w < len[z]; ++w ) {
if(w == len[z] - 1 && a[k + w] == ss[z][w]) {
ok = 0;
break;
}
if(a[k + w] != ss[z][w]) {
break;
}
}
if(!ok)
break;
}
if(!ok) {
sum[i][j]++;
}
}
}
}
// for ( int i = 0; i < main_len; ++i ) {
// for ( int j = 0; j < main_len; ++j ) {
// printf("sum[%d][%d]:%d\n",i, j, sum[i][j]);
// }
// }
for ( int i = 0; i < main_len; ++i ) {
f[i][1] = sum[0][i];
for ( int j = 0; j < i; ++j ) {
for ( int k = 2; k <= min(k, j + 1); ++k ) {
f[i][k] = max(f[i][k], f[j][k - 1] + sum[j + 1][i]);
}
}
}
cout << f[main_len - 1][k] << endl;
return 0;
}
来源:https://www.cnblogs.com/orangeko/p/12537449.html