标签:ott 正整数 isis -- html span class UNC while
给出一个长度不超过 200 的由小写英文字母组成的字母串(约定;该字串以每行 20 个字母的方式输入,且保证每行一定为 20 个)。要求将此字母串分成 k 份( 1<k≤401<k \le 401<k≤40 ),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this中可包含 this 和 is ,选用 this 之后就不能包含 th )。
单词在给出的一个不超过 6 个单词的字典中。
要求输出最大的个数。
每组的第一行有 2 个正整数( p,k )
p表示字串的行数, k 表示分为 k 个部分。
接下来的 p 行,每行均有 20 个字符。
再接下来有 1 个正整数 s ,表示字典中单词个数。( 1≤s≤61 \le s \le 61≤s≤6 )
接下来的 s 行,每行均有 1 个单词。
输出格式:1 个整数,分别对应每组测试数据的相应结果。
1 3
thisisabookyouareaoh
4
is
a
ok
sab
7
this/isabookyoua/reaoh
Solution:
字符串dp,套路就是定义状态$f[i][j]$表示到了母串$i$位置分成$j$块包含的单词最大个数。
对于本题,如果我们知道$s[i][j]$(表示母串$i$到$j$之间的单词个数),则很容易将其转化为区间dp,不难得到状态转移方程:$f[i][j]=max(f[i][j],f[p][j-1]+s[p+1][i]),\;p\in [1,i)$。
那么本题解决关键成了如何预处理$s[i][j]$,我的方法是字符串hash+暴力枚举,对母串和各匹配串均hash一下,然后直接枚举区间和断点,判断hash值是否相等就好了,然后注意细节:1、求hash值时可能爆int 2、每个位置只能做为一个匹配串的开头,匹配到了就直接break掉。
代码:
1 #include<bits/stdc++.h> 2 #define il inline 3 #define ll long long 4 #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) 5 #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--) 6 using namespace std; 7 const int mod=998244353,P=131; 8 int op[205],ha[205],l[7],p[7]; 9 int n,m,k,len,f[205][45],g[205][205]; 10 char s[205],t[205]; 11 bool vis[205]; 12 13 il void init(){ 14 cin>>m>>k; 15 while(m--) For(i,1,20) cin>>s[++len]; 16 op[0]=1; 17 For(i,1,len) op[i]=1ll*op[i-1]*P%mod,ha[i]=(1ll*ha[i-1]*P%mod+s[i]-‘a‘)%mod; 18 cin>>n; 19 For(i,1,n) { 20 cin>>t+1,l[i]=strlen(t+1); 21 For(j,1,l[i]) p[i]=(1ll*p[i]*P+t[j]-‘a‘)%mod; 22 } 23 For(i,1,len) For(j,i,len) For(k,i,j) For(o,1,n) 24 if(k+l[o]-1<=j&&(ha[k+l[o]-1]-1ll*ha[k-1]*op[l[o]]%mod+mod)%mod==p[o]){g[i][j]++;break;} 25 } 26 27 int main(){ 28 ios::sync_with_stdio(0); 29 init(); 30 For(o,1,k) For(i,1,len) For(j,o-1,i-1) f[i][o]=max(f[i][o],f[j][o-1]+g[j+1][i]); 31 cout<<f[len][k]; 32 return 0; 33 }
标签:ott 正整数 isis -- html span class UNC while
原文地址:https://www.cnblogs.com/five20/p/9427021.html