zoukankan      html  css  js  c++  java
  • hdoj5677(manacher+多重背包)

    题目链接:https://vjudge.net/problem/HDU-5677

    题意:给n个字符串,问能不能找出K个这n个串形成的回文字串,使得总长度为L。

    思路:
      首先利用Manacher算法,得到长度为i的回文子串的个数num[i]。要注意bab中的包含bab、b、a、b四个回文串。

      然后就是多重背包,用dp[i][j][k]表示长为1~i的回文串选择j个,总长度为k是否可行。因为每种长度的回文串有多个,就是多重背包,普通做法会超时,利用二进制优化(详见https://www.cnblogs.com/FrankChen831X/p/11423350.html),优化的实质就是把个数i转换成一系列二的次幂的数来代替原来的num[i]。优化之后大概有100*14=1400个回文串,用w1[i]表示其数量,w2[i]表示其长度(等于原长度 ×数量),然后要用滚动数组,不然dp数组的大小为1e7,每个case都memset,可能会超时。

      总复杂度大概为O(K*L*L*logNL)。

    AC code:

      

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    const int maxn=105;
    int T,n,K,L,len,cnt,p[maxn<<1],num[maxn];
    int w1[maxn*20],w2[maxn*20],dp[maxn][maxn];
    char s[maxn<<1],ss[maxn];
    
    void manacher(){
        int mid=0,r=0;
        for(int i=1;i<len;++i){
            if(r>=i) p[i]=min(p[(mid<<1)-i],r-i+1);
            while(s[i-p[i]]==s[i+p[i]]) ++p[i];
            if(i+p[i]>r) r=i+p[i]-1,mid=i;
            ++num[p[i]-1];
        }
        for(int i=1;i<=L;++i)
            num[i]+=num[i+2];
    }
    
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d%d%d",&n,&K,&L);
            memset(dp,0,sizeof(dp));
            cnt=0;
            for(int i=1;i<=n;++i){
                scanf("%s",ss);
                len=strlen(ss);
                s[0]='~',s[1]='|';
                for(int j=0;j<len;++j)
                    s[2*j+2]=ss[j],s[2*j+3]='|';
                len=2*len+2;
                for(int j=0;j<len;++j) p[j]=0;
                manacher();
            }
            for(int i=1;i<=L;++i)
                if(num[i]){
                    for(int j=1;j<=num[i];j<<=1){
                        ++cnt;
                        w1[cnt]=j;
                        w2[cnt]=i*j;
                        num[i]-=j;
                    }
                    if(num[i]){
                        ++cnt;
                        w1[cnt]=num[i];
                        w2[cnt]=i*num[i];
                        num[i]=0;
                    }
                }
            dp[0][0]=1;
            for(int i=1;i<=cnt;++i){
                for(int j=K;j>=w1[i];--j)
                    for(int k=L;k>=w2[i];--k)
                        if(dp[j-w1[i]][k-w2[i]])
                            dp[j][k]=1;
                if(dp[K][L]) break;
            }
            if(dp[K][L]) printf("True
    ");
            else printf("False
    ");
        }
        return 0;
    }
  • 相关阅读:
    各种数据库查询表及表信息的SQL
    多维表头的DataGridView
    SQLite入门笔记
    配置WCF的心得
    JS键盘的键码
    ASP.NET的URL过滤
    利用反射查看类成员
    一个简单的MVC示例
    一个日志类 LogUtil
    一个IniHelper
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/12416879.html
Copyright © 2011-2022 走看看