HDU5677 manacher + 二维多重背包
附上题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5677
问题描述
ztr喜欢研究子串,今天,他有n个串 现在ztr想知道,能否从这n个串的所有回文子串中, 取出恰好k个回文串且满足这些回文串的长度之和为L 以yjqqaq为例 这个串包含的回文子串有 y,j,q,a,q,qq,qaq 所以我们可以既选qq,又选qaq
输入描述
有T组数据,第一行为一个正整数T(T<=10)T(T<=10)
每组数据第一行为三个正整数N(1<=N<=100),K(1<=K<=100),L(L<=100)N(1<=N<=100),K(1<=K<=100),L(L<=100)
接下来N行,每行一个由小写字母构成的字符串,保证每个串的长度不超过L
输出描述
有T行,如果能组成则返回True,反之为False
附上官方题解, 应该很容易懂得,
首先,对于每一个串i跑一次manancher,令g[i][j]=p[j]-1g[i][j]=p[j]−1
这样,g就存储了所有的回文子串的长度
为了方便,把g降到一维表示
不妨把每一个子串抽象成一个物品
费用为二维的,即{长度,1}
价值是Bool型的
这样就成了一个二维判断可行性费用背包问题
设f(i,j)f(i,j)表示当前选出的长度为i,已经选了j个串,这个状态能否达到
f(i,j)=f(i,j)|f(i-g(k),j-1)f(i,j)=f(i,j)∣f(i−g(k),j−1)
这样,时间复杂度为O(L*K*N^{2})O(L∗K∗N2)
显然还是过不去
我们分析,发现其实g是会大量重复的
那么不妨当做多重背包来处理
时间复杂度降为O(L*K*N*log\sum Li)O(L∗K∗N∗log∑Li)
注意: 使用manacher处理的是以某个位置为中心的最长回文串的长度, 我们还应该把这个拆成n个回文串, 假设回文串的长度为奇数那么可以拆成长度为1 3 5... 偶数则可以拆分成2 4 6 ...
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; int N, K, L; int len_num[110]; char str[150]; char a[300]; int p[300]; //manacher void manacher(char *s) { int len = strlen(s+1); int m =2*len + 1; for(int i=1; i<=len; i++){ a[i<<1] = str[i]; a[i<<1|1] = '#'; } a[0] = '+'; a[1] = '#'; a[m+1] = '-'; // cout<<a<<endl; int mx=0, idx; for(int i=1; i<=m; i++){ if(mx > i) p[i] = min(mx-i, p[2*idx-i]); else p[i] = 1; for(; a[i+p[i]]==a[i-p[i]]; p[i]++); if(p[i]+i>mx) mx=p[i]+i, idx=i; if(p[i]-1) { if((p[i]-1)&1) for(int a=1; a<=p[i]-1; a+=2) len_num[a]++; else for(int a=2; a<=p[i]-1; a+=2) len_num[a]++; } } // for(int i=1; i<=m; i++) // cout<<p[i]<<' '; // cout<<endl; } int dp[105][105]; //选择i个字符串能不能组成长度为j的字符串 int main() { int T; scanf("%d", &T); while(T--) { scanf("%d%d%d", &N, &K, &L); memset(len_num, 0, sizeof(len_num)); for(int i=0; i<N; i++) { scanf("%s", str+1); manacher(str); } memset(dp, 0, sizeof(dp)); dp[0][0] = 1; for(int i=1; i<=100; i++){ int num = len_num[i]; for(int k=1; num>0; k<<=1){ //长度为i的选择了mul个 int mul = min(k, num); for(int a=100; a-mul>=0; a--) for(int b=100; b-mul*i>=0; b--){ // dp[a][b] ||= dp[a-mul][b-mul*i]; dp[a][b] = dp[a][b] || dp[a-mul][b-mul*i]; } num -= mul; } } if(dp[K][L]) printf("True\n"); else printf("False\n"); } return 0; }