这个问题可以先处理处一个数组A来
其中A[i] 代表长度为i的回文串有多少个。
那么定义状态d[i][j]代表当前已经选了i个总长度为j这个状态是否可达。问题就是个多重背包。
但是当时做的时候采用了另一个思路,定义d[ i ] [ j ] [ k ] 代表当前只考虑到长度为i 及以上的 回文串选择情况时,当前还剩余j个没有选,需要这j个总长度为k;
那么,最普通的转移就是o(n)就是枚举i长度的选了几个,然后转移到i+1对应的状态,并且这样转移没有办法优化,
那么,我们这样来考虑问题当前还有j个串没有选,不管这一次选了几个长度为i的串,这k个串的长度都将>=i , 我们可以预先将着k个长度都减去1,因为这个1迟早会交付。
所以不管这一次选了几个当前长度的回文串,转移必将如此, d[ i ][ j ][k] = d[i][j][k] | ( d[i + 1][ j - x ] [ k - j ])( x为这次选了几个长度为i的回文串), 那么转移到的状态是第三维恒为定值的一个连续区间,那么就可以通过预处理i + 1 状态的区间和,来更新i的状态,做到O(1)的转移
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int i =0;i<(int)n;i++)
#define rep1(i , x, y) for(int i = (int)x;i<=(int)y;i++)
const int N = 105;
char s[N];
int a[N];
int d[N][N];
int max_ = 0;
void cal(){
int n = strlen(s);
for(int len = 1 ; len <= n ; len ++)
for(int i = 0; i + len <= n; i++){
int j = i + len - 1;
if(len == 1)
d[i][j] = 1;
else if(len == 2){
d[i][j] = (s[i] == s[j]);
} else {
d[i][j] = ((s[i] == s[j]) & d[i + 1][j - 1]);
}
if(d[i][j]){
a[len]++;
max_ = max(max_ , len);
}
}
}
int n , K , L;
bool d2[N][N][N];
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d %d %d",&n ,&K , &L);
memset(a , 0, sizeof(a));
max_ = 0;
for(int i =1 ; i<=n;i++){
scanf("%s",s);
cal();
}
int sum[N][N];
for(int i = max_ + 1 ; i >= 1; i--){
for(int j = 0 ; j<= K; j++)
for(int k =0 ; k <= L; k++){
if(i == max_ + 1){
if(j == 0 && k == 0)
d2[i][j][k] = true;
else d2[i][j][k] = false;
} else {
if(j > k)
d2[i][j][k] = false;
else {
int l = max(0 , j - a[i]) , r= j;
int all = sum[k - j ][r] - (l ==0 ? 0 : sum[k - j][l - 1]);
d2[i][j][k] = (all > 0);
}
}
}
memset(sum , 0, sizeof(sum));
for(int k = 0 ; k<=L; k++)
for(int j = 0 ; j<=K; j++)
sum[k][j] += (j-1 < 0 ? 0 :sum[k][j-1]) + d2[i][j][k];
}
printf("%s\n",d2[1][K][L] ? "True" : "False");
}
return 0;
}