 /**//*
題意:給出一些病毒串,一個原串,問怎么調整原串的順序使得跟最多的病毒串匹配

抄解題報告:
自動機+DP。設主串有na個A、nc個C、ng個G、nt個T,于是題意轉化為用na個A、
nc個C、ng個G、nt個T生成一個新串,使模式串匹配次數最大。先將模式串生成自動機
〔trie圖〕,然后在這上面進行DP,狀態可表示為dp[d,s],d為自動機狀態,
s表示由ma個A、mc個C、mg個G、mt個T的生成串
〔s可表示為ma*(nc+1)*(ng+1)*(nt+1)+mc*(ng+1)*(nt+1)+mg*(nt+1)+mt〕,
轉移為O(4),總復雜度O(500*11^4*4)
那種表示方法就是變進制吧

直接DP(root,s)會超時
Qinz說這題卡時緊
我用他的那種dfs才能過,他是先dfs枚舉出狀態,然后對這個狀態遞推計算,好快

比較神奇,不用管順序的,就記錄個數
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>

using namespace std;

const int MAXN = 520;

struct Node
  {
Node *ch[4] , *next;
int id , match;
};

Node nodes[MAXN] , *Trie , *SuperRoot;
int alloc;

Node *newNode()
  {
memset(&nodes[alloc] , 0 , sizeof(nodes[0]));
nodes[alloc].id = alloc;
return &nodes[alloc++];
}

//int getID(char ch)
//{
// if(ch=='A')return 0;
// if(ch=='C')return 1;
// if(ch=='G')return 2;
// if(ch=='T')return 3;
//}

int getID[200];

void insert(Node * &root, char *s)
  {
if(*s == 0)root->match++;
else
 {
int id = getID[*s];
if(root->ch[id] == 0)root->ch[id] = newNode();
insert(root->ch[id],s+1);
}
}

void buildTrie()
  {
for(int i = 0 ; i<4; i++)
SuperRoot->ch[i] = Trie;
Trie->next = SuperRoot;
queue<Node*>Q;
Q.push(Trie);
while(!Q.empty())
 {
Node *p = Q.front() ; Q.pop();
for(int i = 0 ; i<4; i++)
 {
if(p->ch[i])
 {
p->ch[i]->next = p->next->ch[i];
p->ch[i]->match += p->next->ch[i]->match;//要加這個
Q.push(p->ch[i]);
}
else p->ch[i] = p->next->ch[i];
}
}
}

int val[5];
int num[5];
int dp[MAXN][MAXN*30];


int DP(Node *root, int s)//當前在節點root 剩下可用的字符s 最后能獲得的最大值
  {
if(s==0)return root->match;
int id = root->id;
int &ans = dp[id][s];
if(ans != -1)return ans;//
ans = 0;

int _s = s , t = 1;
for(int i = 3 ; i>= 0;s/=(num[i]+1),i--)
 {
if(s % (num[i]+1) == 0)continue;
ans = max(ans,DP(root->ch[i] , _s - val[i]) );
}
return ans += root->match;//root此時有占一個字符,但不是屬于s的
}

int _num[4];

void dfs(int s , int deep)
  {
if(deep == 4)//dp[i][s] 在節點i處,剩余s個字符的最大值,節點i不占字符
 {
for(int i = 1 ; i < alloc ; i++)
for(int j = 0 ; j < 4; j++)
if(_num[j])
 {
int jj = nodes[i].ch[j]->id;
dp[i][s] = max(dp[i][s] , dp[jj][s-val[j]] + nodes[jj].match);//
}
return ;
}

for(int i = 0 ; i <= num[deep] ; i++ , s+=val[deep])
 {
_num[deep] = i;
dfs(s,deep+1);
}
}

int main()
  {

#ifdef ONLINE_JUDGE
#else
freopen("in","r",stdin);
#endif
getID['A'] = 0;
getID['C'] = 1;
getID['G'] = 2;
getID['T'] = 3;
char str[100];
for(int n ,t = 1;scanf("%d",&n) ,n ; t++)
 {
alloc = 0 ;
SuperRoot = newNode();
Trie = newNode();

for(int i = 0 ; i < n; i++)
 {
scanf("%s",str);
insert(Trie,str);
}

buildTrie();

scanf("%s",str);
memset(num,0,sizeof(num));
for(int i = 0 ; str[i] ; i++)
 {
num[getID[str[i]]]++;
}

int total = 1;
for(int i = 0 ; i < 4; i++)
 {
total *= (num[i]+1);
}
num[4] = 0;
val[4] = 1;
for(int i = 3 ; i>=0 ;i--)
 {
val[i] = val[i+1]*(num[i+1]+1);
}
val[4] = total;

for(int i = 0 ; i < alloc ; i++)
for(int j = 0 ; j <= total ; j++)
dp[i][j] = 0;//-1;
dfs(0,0);
printf("Case %d: %d\n",t,dp[1][total-1]);//DP(Trie,total-1));
}
return 0;
}

|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|