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,而这个滚动可以显著地提高效率。。。(旁白这究竟是为什么呢。。。)
另外一个优化是形如:
加了这个优化之后想法B) 可以卡线过时限。
还有一个需要注意的地方是在构造失配指针的时候要增加一个形如:
的转移,因为在实现AC自动机+DP 的时候不能够像正常的AC自动机(形如HDU2222) 一样递归寻找匹配,所以要把所有p节点的失配节点的匹配统一到节点p上去。
总之。。。 TLE 了很多次 m(-___-)m
这里发的是滚动数组的版本
总之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; }
这里发的是滚动数组的版本
相关文章推荐
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- 简单的四则运算
- 数的奇偶性
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 求两个数的最大公约数【ACM基础题】
- 打印出二进制中所有1的位置
- 杭电题目---一只小蜜蜂
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- NWERC2010 NKOJ2178 Stock Prices
- 2011ACM福州网络预选赛B题 HDU4062 Abalone
- Codeforces Round #197 (Div. 2)
- Codeforces Round #198 (Div. 1)
- ACM常用算法
- 2013 Multi-University Training Contest 1
- ACM/ICPC需要掌握的知识[转载]
- 北大—1006——Biorhythms