【hdu3341-Lost's revenge】DP压缩+AC自动机
题意:给定只含有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自动机的更多相关文章
- Lost's revenge HDU - 3341 AC自动机+DP(需要学会如何优雅的压缩状态)
题意: 给你n个子串和一个母串,让你重排母串最多能得到多少个子串出现在重排后的母串中. 首先第一步肯定是获取母串中每个字母出现的次数,只有A T C G四种. 这个很容易想到一个dp状态dp[i][A ...
- ZOJ 3494 BCD Code (数位DP,AC自动机)
题意: 将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的.给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的 ...
- 【HDOJ5955】Guessing the Dice Roll(概率DP,AC自动机,高斯消元)
题意: 有n个人,每个人有一个长为L的由1~6组成的数串,现在扔一个骰子,依次记录扔出的数字,如果当前扔出的最后L个数字与某个人的数串匹配,那么这个人就算获胜,现在问每个人获胜的概率是多少. n,l& ...
- 计蒜客-蒜场抽奖(AC自动机+状态压缩DP)
题解:题意不再说了,题目很清楚的. 思路:因为N<=10,所以考虑状态压缩 AC自动机中 val[1<<i]: 表示第i个字符串.AC自动机中fail指针是指当前后缀在其他串里面所能 ...
- 【AC自动机】【状压dp】hdu2825 Wireless Password
f(i,j,S)表示当前字符串总长度为i,dp到AC自动机第j个结点,单词集合为S时的方案数. 要注意有点卡常数,注意代码里的注释. #include<cstdio> #include&l ...
- 【原创】AC自动机小结
有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配. AC自动机 其实 就是创建了一个状态的转移图,思想很 ...
- 转自kuangbin的AC自动机(赛前最后一博)
有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配. AC自动机 其实 就是创建了一个状态的转移图,思想很 ...
- 【AC自动机&&Trie图】积累
以前KMP和后缀系列(主要是后缀数组,后缀自动机),都刷了一定数量的题,但是对于AC自动机,却有些冷落,罪过. 但是我感觉,在蓝桥杯比赛中AC自动机出现的概率比后缀系列大,简单的会考匹配,稍难一点会考 ...
- HDU 4511 (AC自动机+状态压缩DP)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4511 题目大意:从1走到N,中间可以选择性经过某些点,比如1->N,或1->2-> ...
随机推荐
- discuz分类信息地区联动菜单字段
1 = 河南省 1.1 = 郑州市 1.1.1 = 中原区 1.1.2 = 二七区 1.1.3 = 管城区 1.1.4 = 金水区 1.1.5 = 上街区 1.1.6 = 惠济区 1.1.7 = 巩义 ...
- “psp”软件需求规约
1 系统概述 1.1 概述 该产品是基于软件开发的个人软件过程(personal software process)系统.基本信息有软件开发人员,项目经理,研发经理和管理层登录系统后根据各自的相应权限 ...
- [原]Java修炼 之 基础篇(一)Java语言特性
学习软件开发,首先要选择的就是选择需要采用的编程语言,考虑语言本身的优缺点和实际需求,综合评价之后选择相关的语言进行系统开发.本篇博客开始就从近年来比较流行的Java开始为大家讲起. 背景 1995年 ...
- UML 中的用例图解析以及starUML详细介绍
UML中的用例(Use Case)概念分析及StarUML实例 在UML 中use case 似 乎最簡單的,用例建模的最主要功能就是用来表达系统的功能性需求或行为,依我的理解用例建模可分为用例图和用 ...
- WPF多窗口传参解决方案
在WPF中,我们的常常涉及到多个窗口之间传递参数的问题,那么该怎么传呢? 答:在窗口的构造函数中添加想要传递的参数.(我表达不好,请看下面代码.) 在主窗口中单机button打开一个新的子窗口. // ...
- Vue2.0的通用组件
饿了么基于Vue2.0的通用组件开发之路(分享会记录) Element:一套通用组件库的开发之路 Element 是由饿了么UED设计.饿了么大前端开发的一套基于 Vue 2.0 的桌面端组件库. ...
- C++中的运算符重载注意事项
1.C++中的运算符重载的方式有三种: a.类成员函数重载 b.友元函数重载 c.普通函数重载 注意: a.我们主要使用的方式主要是用:类成员函数和友元函数来实现运算符的重载. b.其实用普通函数理论 ...
- JPA学习---第十二节:JPA中的联合主键
1.定义实体类,代码如下: (1).将联合主键放到一个类中,代码如下: package learn.jpa.entity; import java.io.Serializable; import ja ...
- token验证-微信公众平台开发3(asp.net)
童鞋们直接看代码吧:(我这里是ashx处理程序写的类,开发过网站的一般都知道) <%@ WebHandler Language="C#" class="weixin ...
- How to Build FFmpeg for Android
http://www.roman10.net/how-to-build-ffmpeg-for-android/ ffmpeg is an open-source platform for record ...