zoukankan      html  css  js  c++  java
  • 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*logsum 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
    ");
            else
                printf("False
    ");
        }
        return 0;
    }
  • 相关阅读:
    Android自己定义组件系列【2】——Scroller类
    ostringstream的使用方法
    什么是Spring?Spring是什么?
    天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行拂乱其所为,所以动心忍性,增益其所不能
    伤不起的戴尔台式机XPS8700脆弱的蓝牙
    cocos2d-x-3.1 事件分发机制 (coco2d-x 学习笔记七)
    SSL工作原理
    AfxMessageBox和MessageBox差别
    oninput,onpropertychange,onchange的使用方法和差别
    Bootstrap网站模板
  • 原文地址:https://www.cnblogs.com/xingxing1024/p/5459844.html
Copyright © 2011-2022 走看看