题目描述
给出一个长度不超过200200的由小写英文字母组成的字母串(约定;该字串以每行2020个字母的方式输入,且保证每行一定为2020个)。要求将此字母串分成kk份(1<k le 401<k≤40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串thisthis中可包含thisthis和isis,选用thisthis之后就不能包含thth)。
单词在给出的一个不超过66个单词的字典中。
要求输出最大的个数。
输入输出格式
输入格式:
每组的第一行有22个正整数(p,kp,k)
pp表示字串的行数,kk表示分为kk个部分。
接下来的pp行,每行均有2020个字符。
再接下来有11个正整数ss,表示字典中单词个数。(1 le s le 61≤s≤6)
接下来的ss行,每行均有11个单词。
输出格式:
11个整数,分别对应每组测试数据的相应结果。
输入输出样例
说明
this/isabookyoua/reaoh
一共有两个dp
第一个dp为预处理
必须要从后往前转移 这样遇到重复的也不会影响结果 如题意所得 每次判断具有后效性 所以当遇到这种情况的时候一定要从后往前dp 之前有一道安排工作的dp也是一样!!!!
注意 预处理的细节
第二个dp为区间dp
划分为k个区域只要加上k-1个隔板即可
然后就是注意 区间dp各种小细节!!!!
我做的时候有个疑问 为什么分隔点是s后面 而不能是当前处理区间末尾的前面一格分隔
想一想就知道是错的!!!。。。
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// #define inf 2147483647 #define N 200 string s; int k,q,n; string table[10]; int word[210][210]; int dp[210][50]; int len; bool check(int i,int j) { /* string temp=s.substr(i,j-i+1); rep(i,1,q)if(temp.find(table[i])==0)return true; */ rep(k,1,q) { if(table[k].size()>j-i+1)continue; if(s.substr(i,table[k].size())==table[k])return true; } return false; } void init() { repp(j,len,1) repp(i,j,1) { word[i][j]=word[i+1][j]; if(check(i,j))word[i][j]++; } } int main() { RII(n,k); rep(i,1,n) { string temp; cin>>temp; s+=temp; } len=s.size(); s='*'+s;//方便处理 RI(q); rep(i,1,q) cin>>table[i]; init(); rep(i,1,len) dp[i][0]=word[1][i]; rep(i,1,k-1)//加入k-1个 分隔 最后就会分成k块 rep(j,i+1,len) rep(s,i,j-1)//枚举断点s dp[j][i]=max(dp[j][i],dp[s][i-1]+word[s+1][j]);//我不知道为什么改成word[s][j-1]不行 (否则 第三层循环没有任何意义 答案永远不会再更新了!!!) cout<<dp[len][k-1]; }