hdu 3341 AC自動機+DP
阿新 • • 發佈:2018-11-14
這道題就是給你N個單詞,一個字串,問你將這個字串重排,能組成包含單詞次數最多的字串有多長。這道題的DP方程有點意思,因為只有四個字元ACGT,字串的長度不超過40,所以我們可以dp[i][a][b[][c][d],表示匹配了i個字元,用了分別用了a,b,c,d個ACGT,能包含的最多的單詞。其中但dp[500][40][40][40][40]的記憶體,但我們可以轉化一下,轉化成在某種進位制下的計數,我們首先計算每個字元在字串中出現的次數,每增加一個A,我們就增加(num[1]+1)(num[2]+1)(num[3]+1),每增加一個T,我們就增加(num[2]+1)*(num[3]+1),每增加一個C,我們就增加(num[3]+1),每增加一個G,我們就增加1,和狀態壓縮的意思差不多,DP方程列出來就好說了
#include<bits/stdc++.h> using namespace std; using LL = int64_t; const int maxnode=5005; const int sigma_size=4; char s[maxnode]; struct Node { int son[sigma_size]; int val,fail; }ch[maxnode]; int dp[505][15000]; struct AC { int sz=1; queue<int>Q; void init(int x) {ch[x].fail=ch[x].val=0;memset(ch[x].son,0,sizeof(ch[x].son));} int idx(char c) { if(c=='A') return 0; if(c=='T') return 1; if(c=='C') return 2; if(c=='G') return 3; } void insert(char s[],int v) { int u=0,n=strlen(s); for(int i=0;i<n;i++) { int c=idx(s[i]); if(!ch[u].son[c]) { init(sz); ch[u].son[c]=sz++; } u=ch[u].son[c]; } ch[u].val++; } void build() { for(int i=0;i<sigma_size;i++) if(ch[0].son[i]) Q.push(ch[0].son[i]); while(!Q.empty()) { int now=Q.front();Q.pop(); int fail=ch[now].fail; ch[now].val+=ch[fail].val; for(int i=0;i<sigma_size;i++) { int nxt=ch[now].son[i]; if(nxt) { ch[nxt].fail=ch[fail].son[i]; Q.push(nxt); } else ch[now].son[i]=ch[fail].son[i]; //ch[ch[now].son[i]].val=ch[ch[ch[now].fail].son[i]].val; } } } int solve(char s[]) { int num[5]={0},cnt[5],len=strlen(s); for(int i=0;i<len;i++) num[idx(s[i])]++; cnt[0]=(num[1]+1)*(num[2]+1)*(num[3]+1); cnt[1]=(num[2]+1)*(num[3]+1); cnt[2]=(num[3]+1); cnt[3]=1; memset(dp,-1,sizeof(dp)); dp[0][0]=0; for(int a=0;a<=num[0];a++) { for(int b=0;b<=num[1];b++) { for(int c=0;c<=num[2];c++) { for(int d=0;d<=num[3];d++) { int now=a*cnt[0]+b*cnt[1]+c*cnt[2]+d*cnt[3]; for(int i=0;i<sz;i++) { if(dp[i][now]>=0) { for(int k=0;k<4;k++) { if(k==0&&a==num[0]) continue; if(k==1&&b==num[1]) continue; if(k==2&&c==num[2]) continue; if(k==3&&d==num[3]) continue; dp[ch[i].son[k]][now+cnt[k]]=max(dp[ch[i].son[k]][now+cnt[k]],dp[i][now]+ch[ch[i].son[k]].val); } } } } } } } int ans=0,now=0; for(int i=0;i<4;i++) now=now+num[i]*cnt[i]; for(int i=0;i<sz;i++) ans=max(ans,dp[i][now]); return ans; } }; int main() { ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); int n,kase=1; while(cin>>n&&n) { AC ans;ans.init(0); for(int i=1;i<=n;i++) { cin>>s; ans.insert(s,1); } ans.build(); cin>>s; cout<<"Case "<<kase++<<": "<<ans.solve(s)<<"\n"; } return 0; }