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(ig(k),j1)

这样,时间复杂度为O(L*K*N^{2})O(LKN2​​)

显然还是过不去

我们分析,发现其实g是会大量重复的

那么不妨当做多重背包来处理

时间复杂度降为O(L*K*N*log\sum Li)O(LKNlogLi)

注意: 使用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;
}

 

posted @ 2016-05-04 21:28  xing-xing  阅读(182)  评论(0编辑  收藏  举报