1. 程式人生 > >[BZOJ 1195] 最短母串

[BZOJ 1195] 最短母串

limits display 定義 limit set www 選擇 -s oid

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

Solution:

看到數據範圍n<=12,就要往狀壓DP上想

為了保證後項無關性,首先肯定要先將已被包含的子串去除,這樣保證兩個串合並時必然是首尾相接

接下來預處理出第i個串接在第j個串後需要的長度,暴力即可

記錄最後一個串,設 dp[s][i] 為選擇的集合為 s,最後一個串是 i 的最小長度,枚舉前一個串進行轉移即可

此題要求保證字典序最小,因此要同時記錄 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\n",res.s); return 0; }

Review:

1、根據特殊數據範圍 -----> 推測算法

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

char數組在函數內部是亂碼,要用memset(char,‘\0‘,sizeof(char))才能清零

如在函數內新定義的一定要初始化

3、在需要多次對結構體進行比較時,不妨使用重載運算符+min/max的方式

4、如空間卡得很死,將int--->short,long long ---> long

5、要保持字典序最小,最樸素的方式就是將字符串存儲下來

[BZOJ 1195] 最短母串