题意:给定只含有A、G、C、T的n个模板串,一个文本串,文本串任意两个字母可互换位置,问最多能匹配多少个模板串。
注意:匹配同一个模板串匹配了两次,ans+=2;(可重复)

题解:

原本想到一个简单dp : 开一个数组d[t1][t2][t3][t4][x],t1~t4分别表示4个字母各有多少个,x表示当前位置。

然后这个数组为40*40*40*40*600,各种爆空间。

后来才知道要用压缩。。。

比如ACGT分别有5,6,7,8个。那t1为6进制,可以放0~5,t2为7进制……

然后类比10进制,把它压成一个10进制的数,这个数最大是11*11*11*11=14641.

压缩的原理:

我打了两个程序,dp一个用了递归,一个用了for循环,递归那个一直超时,for那个就A了。递归跟for循环差别这么大吗?

 //DP为for循环递推形式 AC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std; const int N=;
struct node{
int sum,fail,son[];
}a[N];
queue<int> q;
char s[N];
int n,num,t[],sum[],k[],d[][N];
bool vis[][N];
int maxx(int x,int y){return x>y ? x:y;}
int minn(int x,int y){return x<y ? x:y;} int idx(char c)
{
if(c=='A') return ;
if(c=='G') return ;
if(c=='C') return ;
if(c=='T') return ;
} void clear(int x)
{
a[x].fail=a[x].sum=;
memset(a[x].son,,sizeof(a[x].son));
} void trie(char *c)
{
int x=,l=strlen(c);
for(int i=;i<l;i++)
{
int ind=idx(c[i]);
if(!a[x].son[ind])
{
num++;
clear(num);
a[x].son[ind]=num;
}
x=a[x].son[ind];
}
a[x].sum++;
} void buildAC()
{
while(!q.empty()) q.pop();
for(int i=;i<=;i++)
if(a[].son[i]) q.push(a[].son[i]);
while(!q.empty())
{
int x=q.front();q.pop();
int fail=a[x].fail;
for(int i=;i<=;i++)
{
if(a[x].son[i])
{
int y=a[x].son[i],z=a[fail].son[i];
a[y].fail=z;
a[y].sum+=a[z].sum;
q.push(a[x].son[i]);
}
else a[x].son[i]=a[fail].son[i];
}
}
} int makeup()
{
return t[]*k[]+t[]*k[]+t[]*k[]+t[]*k[];
} int dp()
{
memset(d,-,sizeof(d));
d[][]=;
int ans=,ss=sum[]+sum[]+sum[]+sum[];
for(int l=;l<=ss;l++)//当前选择了多少个点
for(int i=;i<=num;i++)//当前走到了第i个点
for(t[]=maxx(,l-sum[]-sum[]-sum[]);t[]<=minn(l,sum[]);t[]++)//限制 最少选多少 最多选多少
for(t[]=maxx(,l-t[]-sum[]-sum[]);t[]<=minn(l,sum[]);t[]++)
for(t[]=maxx(,l-t[]-t[]-sum[]);t[]<=minn(l,sum[]);t[]++)
{
t[]=l-t[]-t[]-t[];
int now=makeup();
if(d[now][i]==-) continue;
ans=maxx(ans,d[now][i]);
for(int j=;j<=;j++)
{
int y=a[i].son[j];
if(t[j]+<=sum[j])
{
t[j]++;
int next=makeup();
d[next][y]=maxx(d[next][y],d[now][i]+a[y].sum);
t[j]--;
}
}
}
return ans;
} int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int T=;
while()
{
scanf("%d",&n);
if(!n) return ;
num=;
clear();
for(int i=;i<=n;i++)
{
scanf("%s",s);
trie(s);
}
buildAC();
scanf("%s",s);
int mx=,l=strlen(s);
memset(sum,,sizeof(sum));
memset(vis,,sizeof(vis));
for(int i=;i<l;i++) sum[idx(s[i])]++;
for(int i=;i<=;i++)
{
k[i]=;
for(int j=i+;j<=;j++)
k[i]*=(sum[j]+);
mx+=k[i]*sum[i];
}
printf("Case %d: %d\n",++T,dp());
}
return ;
}
 //DP为递归形式 TLE
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std; const int N=;
struct node{
int sum,fail,son[];
}a[N];
queue<int> q;
char s[N];
int n,num,t[],sum[],k[],d[][N];
bool vis[][N];
int maxx(int x,int y){return x>y ? x:y;}
int minn(int x,int y){return x<y ? x:y;} int idx(char c)
{
if(c=='A') return ;
if(c=='G') return ;
if(c=='C') return ;
if(c=='T') return ;
} void clear(int x)
{
a[x].fail=a[x].sum=;
memset(a[x].son,,sizeof(a[x].son));
} void trie(char *c)
{
int x=,l=strlen(c);
for(int i=;i<l;i++)
{
int ind=idx(c[i]);
if(!a[x].son[ind])
{
num++;
clear(num);
a[x].son[ind]=num;
}
x=a[x].son[ind];
}
a[x].sum++;
} void buildAC()
{
while(!q.empty()) q.pop();
for(int i=;i<=;i++)
if(a[].son[i]) q.push(a[].son[i]);
while(!q.empty())
{
int x=q.front();q.pop();
int fail=a[x].fail;
for(int i=;i<=;i++)
{
if(a[x].son[i])
{
int y=a[x].son[i],z=a[fail].son[i];
a[y].fail=z;
a[y].sum+=a[z].sum;
q.push(a[x].son[i]);
}
else a[x].son[i]=a[fail].son[i];
}
}
} int makeup(int t1,int t2,int t3,int t4)
{
return t1*k[]+t2*k[]+t3*k[]+t4*k[];
} int dp(int now,int x)
{
int ans=,t1,t2,t3,t4;
if(vis[now][x]) return d[now][x];
t4=now%k[];
t3=((now%k[])-(t4*k[]))/k[];
t2=((now%k[])-(t4*k[]+t3*k[]))/k[];
t1=(now-(t4*k[]+t3*k[]+t2*k[]))/k[];
for(int i=;i<=;i++)
{
int y=a[x].son[i];
if(!y && x) ans=maxx(ans,dp(now,));
else if(i== && t1>=) ans=maxx(ans,a[y].sum+dp(makeup(t1-,t2,t3,t4),y));
else if(i== && t2>=) ans=maxx(ans,a[y].sum+dp(makeup(t1,t2-,t3,t4),y));
else if(i== && t3>=) ans=maxx(ans,a[y].sum+dp(makeup(t1,t2,t3-,t4),y));
else if(i== && t4>=) ans=maxx(ans,a[y].sum+dp(makeup(t1,t2,t3,t4-),y));
}
d[now][x]=maxx(d[now][x],ans);
vis[now][x]=;
return d[now][x];
} int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int T=;
while()
{
scanf("%d",&n);
if(!n) return ;
num=;
clear();
for(int i=;i<=n;i++)
{
scanf("%s",s);
trie(s);
}
buildAC();
scanf("%s",s);
int mx=,l=strlen(s);
memset(sum,,sizeof(sum));
memset(vis,,sizeof(vis));
for(int i=;i<l;i++) sum[idx(s[i])]++;
for(int i=;i<=;i++)
{
k[i]=;
for(int j=i+;j<=;j++)
k[i]*=(sum[j]+);
mx+=k[i]*sum[i];
}
// printf("%d\n",dp());
memset(d,,sizeof(d));
printf("Case %d: %d\n",++T,dp(mx,));
}
return ;
}

【hdu3341-Lost's revenge】DP压缩+AC自动机的更多相关文章

  1. Lost's revenge HDU - 3341 AC自动机+DP(需要学会如何优雅的压缩状态)

    题意: 给你n个子串和一个母串,让你重排母串最多能得到多少个子串出现在重排后的母串中. 首先第一步肯定是获取母串中每个字母出现的次数,只有A T C G四种. 这个很容易想到一个dp状态dp[i][A ...

  2. ZOJ 3494 BCD Code (数位DP,AC自动机)

    题意: 将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的.给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的 ...

  3. 【HDOJ5955】Guessing the Dice Roll(概率DP,AC自动机,高斯消元)

    题意: 有n个人,每个人有一个长为L的由1~6组成的数串,现在扔一个骰子,依次记录扔出的数字,如果当前扔出的最后L个数字与某个人的数串匹配,那么这个人就算获胜,现在问每个人获胜的概率是多少. n,l& ...

  4. 计蒜客-蒜场抽奖(AC自动机+状态压缩DP)

    题解:题意不再说了,题目很清楚的. 思路:因为N<=10,所以考虑状态压缩 AC自动机中 val[1<<i]: 表示第i个字符串.AC自动机中fail指针是指当前后缀在其他串里面所能 ...

  5. 【AC自动机】【状压dp】hdu2825 Wireless Password

    f(i,j,S)表示当前字符串总长度为i,dp到AC自动机第j个结点,单词集合为S时的方案数. 要注意有点卡常数,注意代码里的注释. #include<cstdio> #include&l ...

  6. 【原创】AC自动机小结

    有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配.           AC自动机 其实 就是创建了一个状态的转移图,思想很 ...

  7. 转自kuangbin的AC自动机(赛前最后一博)

    有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配.           AC自动机 其实 就是创建了一个状态的转移图,思想很 ...

  8. 【AC自动机&&Trie图】积累

    以前KMP和后缀系列(主要是后缀数组,后缀自动机),都刷了一定数量的题,但是对于AC自动机,却有些冷落,罪过. 但是我感觉,在蓝桥杯比赛中AC自动机出现的概率比后缀系列大,简单的会考匹配,稍难一点会考 ...

  9. HDU 4511 (AC自动机+状态压缩DP)

    题目链接:  http://acm.hdu.edu.cn/showproblem.php?pid=4511 题目大意:从1走到N,中间可以选择性经过某些点,比如1->N,或1->2-> ...

随机推荐

  1. zookeeper的一些异常总结

    1.Could not find the main class: org.apache.zookeeper.server.quorum.QuorumPeerMain.  Program will ex ...

  2. iOS学习之UI自定义cell

    一.自定义Cell 为什么需要自定义cell:系统提供的cell满足不了复杂的样式,因此:自定义Cell和自定义视图一样,自己创建一种符合我们需求的Cell并使用这个Cell.如下图所示的这些Cell ...

  3. win7虚拟打印驱动开发注意事项

    win7虚拟打印驱动开发注意事项 通过控制面板安装遇到以下问题:错误1.提示“Printer driver was not installed. Operation could not be comp ...

  4. 2.Modelsim打开时出现的Error

    Modelsim之error “unable to check out a viewer license necessary for use of the modelsim graph.Vsim is ...

  5. C++变量的存储类别与作用域

    总结一下C++中变量的存储类别以及变量的作用域. (1)标示符的存储类别决定了标示符在内存中存在的时间(我们可以理解标示符就是确定一个变量的符号,也就是我们所说的变量名) 二:存储类别 (1)静态存储 ...

  6. C++中的链表节点用模板类和用普通类来实现的区别

    C++中的链表节点通常情况下类型都是一致的.因此我们可以用模板来实现. #include <iostream> using namespace std; template<typen ...

  7. python中fork()函数生成子进程分析

    python的os module中有fork()函数用于生成子进程,生成的子进程是父进程的镜像,但是它们有各自的地址空间,子进程复制一份父进程内存给自己,两个进程之 间的执行是相互独立的,其执行顺序可 ...

  8. Class.forName("com.mysql.jdbc.Driver");的作用

    对于大的项目当然我们都已经有了原有基本框架,但是对于一些新的技术探讨的时候,我们还是直接调用Class.forName("com.mysql.jdbc.Driver")连接数据库进 ...

  9. 【Subsets II】cpp

    题目: Given a collection of integers that might contain duplicates, nums, return all possible subsets. ...

  10. Leetcode#59 Spiral Matrix II

    原题地址 相比于Spiral Matrix(参见这篇文章)要简单一些,因为是方阵,所以代码简洁一些. 注意当n是奇数的时候,中心小块要单独赋值(代码21行) 代码: vector<vector& ...