您的位置:首页 > 其它

HDU4758 AC自动机+DP (HDU4758与HDU2222)

2013-10-11 21:07 477 查看
竟然刚刚发现滚动数组可以提高DP 的效率(旁白:这是为什么呢。。。)

总之DP 的题目实现效率非常重要啊ORZ

HDU4758 求一个含有固定数量的0和固定数量的1 的01串,使得其包含两个给定的子串(子串不会相同)

最终的解决方案是用两个子串建立一个AC自动机,然后利用:

A) dp[L][n1][k][s] 其中L 表示当前dp状态串的总长,n1表示1的个数,k表示当前状态在AC自动机中的位置,s是一个二进制状压的量,表示有几个子串被满足;

B) dp[n0][n1][k][s] n0表示当前0的个数 后面的同上。

一般地说B) 是比较直观的想法,不过A) 的好处是可以使用一个滚动数组把第一个维度的大小缩小为2,而这个滚动可以显著地提高效率。。。(旁白这究竟是为什么呢。。。)

另外一个优化是形如:

if(dp[i][j][k][e]==0) continue;

加了这个优化之后想法B) 可以卡线过时限。

还有一个需要注意的地方是在构造失配指针的时候要增加一个形如:

tree[p].val |= tree[tree[p].fal].val;


的转移,因为在实现AC自动机+DP 的时候不能够像正常的AC自动机(形如HDU2222) 一样递归寻找匹配,所以要把所有p节点的失配节点的匹配统一到节点p上去。

总之。。。 TLE 了很多次 m(-___-)m

#include<cstdio>
#include<cstring>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;

const int Maxm = 200;
const int Maxn = 100;
const int An = 2;
const int inf = 1000000007;
const char Char0 = 'a';

int tn;

struct node{
int fal,val;
int s[An+1];
void init(){
fal=val=0;
memset(s,0,sizeof(s));
}
}tree[Maxm+10];

void init_tree(){
tree[0].init();
tn=1;
}

void insert_word(int id,char *str){
int lb=0;
while(*str){
if(!tree[lb].s[*str-Char0]){
tree[tn].init();
tree[lb].s[*str-Char0]=tn++;
}
lb=tree[lb].s[*str-Char0];
++str;
}
tree[lb].val |= id;
}

void build_tree(){
queue<int> q;
while(!q.empty()) q.pop();
q.push(0);
while(!q.empty()){
int p=q.front();
if(tree[p].fal==p) tree[p].fal=0;
for(int i=0;i<An;i++)
if(tree[p].s[i]){
tree[tree[p].s[i]].fal=tree[tree[p].fal].s[i];
tree[tree[p].s[i]].val |= tree[tree[tree[p].s[i]].fal].val;
q.push(tree[p].s[i]);
}else tree[p].s[i]=tree[tree[p].fal].s[i];
q.pop();
}
}

int dp[2][Maxn+10][Maxm+10][4]; // X Y Automaton_ID Matching
int t,n,m;
char s[200];

int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
gets(s);
init_tree();
gets(s);
for(int i=0;i<strlen(s);i++)
if(s[i]=='R') s[i]='a';
else s[i]='b';
insert_word(1,s);
/*
for(int i=0;i<strlen(s);i++)
printf("%c",s[i]);
printf("\n");
*/
gets(s);
for(int i=0;i<strlen(s);i++)
if(s[i]=='R') s[i]='a';
else s[i]='b';
insert_word(2,s);
/*
for(int i=0;i<strlen(s);i++)
printf("%c",s[i]);
printf("\n");
*/
build_tree();
memset(dp,0,sizeof(dp));
dp[0][0][0][0]=1;
for(int i=0;i<=n+m;i++){
memset(dp[(i+1)&1],0,sizeof(dp[(i+1)&1]));
for(int j=0;j<=m;j++)
for(int k=0;k<tn;k++)
for(int e=0;e<4;e++){
dp[(i+1)&1][j][tree[k].s[0]][e | tree[tree[k].s[0]].val]+=dp[i&1][j][k][e];
dp[(i+1)&1][j][tree[k].s[0]][e | tree[tree[k].s[0]].val]%=inf;
if(j==m) continue;
dp[(i+1)&1][j+1][tree[k].s[1]][e | tree[tree[k].s[1]].val]+=dp[i&1][j][k][e];
dp[(i+1)&1][j+1][tree[k].s[1]][e | tree[tree[k].s[1]].val]%=inf;
}
}
int ans=0;
for(int i=0;i<tn;i++){
ans+=dp[(n+m)&1][m][i][3];
ans%=inf;
}
printf("%d\n",ans);
}
return 0;
}


这里发的是滚动数组的版本
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  AC自动机 DP acm