zoukankan      html  css  js  c++  java
  • [BZOJ 1195] 最短母串

    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1195

    Solution:

    看到数据范围n<=12,就要往状压DP上想

    为了保证后项无关性,首先肯定要先将已被包含的子串去除,这样保证两个串合并时必然是首尾相接

    接下来预处理出第i个串接在第j个串后需要的长度,暴力即可

    记录最后一个串,设 dp[s][i] 为选择的集合为 s,最后一个串是 的最小长度,枚举前一个串进行转移即可

    此题要求保证字典序最小,因此要同时记录 dp[s][i] 的字符串

    时间复杂度:上界为O(N^2 · 2^N * 600),但明显达不到这个上界,n=12 时应为 4e7 左右

    空间复杂度:此题空间卡得很紧,最好将int换为short

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF=700;
    
    struct sub
    {
        short len;
        char s[602];
        void read(){scanf("%s",s);len=strlen(s);}
        bool operator < (const sub &t) const  //重载小于号方便使用min
        {
            if (len!=t.len) return len<t.len;
            for(int i=0;i<len;i++)
                if(s[i]!=t.s[i]) return s[i]<t.s[i];
            return true;
        }
    }dp[4098][13],dat[13],res;
    
    short n,ban[13],pre[13][13],cnt=0;
    
    bool ide(int l,int posl,int r)
    {
        for(int posr=0;posr<dat[r].len && posl<dat[l].len;posr++,posl++)
            if(dat[l].s[posl]!=dat[r].s[posr]) return false;
        return true;
    }
    
    bool overlap(int l,int r)
    {
        if(dat[l].len<dat[r].len) return false;
        for(int i=0;i<=dat[l].len-dat[r].len;i++)
            if(ide(l,i,r)) return true;
        return false;
    }
    
    int cal(int l,int r)
    {
        for(int i=max(0,dat[l].len-dat[r].len);i<dat[l].len;i++)
            if(ide(l,i,r)) return dat[r].len-(dat[l].len-i);
        return dat[r].len;
    }
    
    sub new_node(int S,int u,int v)
    {
        sub ret=dp[S][u];
        for(int i=dat[v].len-pre[u][v];i<dat[v].len;i++) 
            ret.s[ret.len++]=dat[v].s[i];
        return ret;
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) dat[i].read();
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(overlap(i,j) && i!=j && (!overlap(j,i) || i>j)) ban[j]=true;  //对二者相同的特判
        
        for(int i=1;i<=n;i++) 
            if(!ban[i]) dat[++cnt]=dat[i];
            
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                if(i!=j) pre[i][j]=cal(i,j);  //预处理出j接在i后需要多长
    
        for(int i=0;i<(1<<n);i++) for(int j=0;j<=n;j++) dp[i][j].len=INF;
        for(int i=1;i<=cnt;i++) dp[1<<(i-1)][i]=dat[i];
        
        for(int i=1;i<(1<<cnt);i++)
            for(int j=1;j<=cnt;j++) if(i&(1<<(j-1)))
                for(int k=1;k<=cnt;k++) if(!(i&(1<<(k-1))))
                    dp[i|(1<<(k-1))][k]=min(dp[i|(1<<(k-1))][k],new_node(i,j,k));
        
        res.len=INF;
        for(int i=1;i<=cnt;i++)
            res=min(res,dp[(1<<cnt)-1][i]);
        printf("%s
    ",res.s);
        
        return 0;
    }

    Review:

    1、根据特殊数据范围   ----->   推测算法

    2、非全局变量的初始化:

    char数组在函数内部是乱码,要用memset(char,'',sizeof(char))才能清零

    如在函数内新定义的一定要初始化

    3、在需要多次对结构体进行比较时,不妨使用重载运算符+min/max的方式

    4、如空间卡得很死,将int--->short,long long ---> long

    5、要保持字典序最小,最朴素的方式就是将字符串存储下来

  • 相关阅读:
    ubuntu 12.04安装redis2.6.16
    linux常用命令(自我积累)-two
    计算机历年考研复试上机基础题(一)
    PhantomJS爬虫 -----全国高校查询-------计算机科学与技术前50大学
    python3爬虫 -----爬取大学信息并通过matplotlib与numpy绘制结果-----from最好大学网
    51Nod 1265 四点共面
    算法训练 出现次数最多的整数
    51nod 1101 换零钱 (完全背包)
    Poj3624 Charm Bracelet (01背包)
    Poj1258 Agri-Net (最小生成树 Prim算法 模板题)
  • 原文地址:https://www.cnblogs.com/newera/p/9098603.html
Copyright © 2011-2022 走看看