HDU4758 Walk Through Squares(AC自动机+状压DP)

题目大概说有个n×m的格子,有两种走法,每种走法都是一个包含D或R的序列,D表示向下走R表示向右走。问从左上角走到右下角的走法有多少种走法包含那两种走法。

D要走n次,R要走m次,容易想到用AC自动机上的DP解决:

  • 用两种走法的序列构造AC自动机
  • dp[i][j][S][k]表示D用了i个R用了j个,且当前走到自动机的S结点,已经包含的走法的状态集合是k的方案数
  • 转移就是走R或者走D了,我用我为人人来转移
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<queue>
 4 #include<algorithm>
 5 using namespace std;
 6 #define MAXN 222
 7 int tn,ch[MAXN][2],fail[MAXN],flag[MAXN];
 8 int idx[128];
 9 void insert(char *s,int k){
10     int x=0;
11     for(int i=0; s[i]; ++i){
12         int y=idx[s[i]];
13         if(ch[x][y]==0) ch[x][y]=++tn;
14         x=ch[x][y];
15     }
16     flag[x]|=(1<<k);
17 }
18 void getfail(){
19     memset(fail,0,sizeof(fail));
20     queue<int> que;
21     for(int i=0; i<2; ++i){
22         if(ch[0][i]) que.push(ch[0][i]);
23     }
24     while(!que.empty()){
25         int x=que.front(); que.pop();
26         for(int i=0; i<2; ++i){
27             if(ch[x][i]){
28                 que.push(ch[x][i]);
29                 fail[ch[x][i]]=ch[fail[x]][i];
30                 flag[ch[x][i]]|=flag[ch[fail[x]][i]];
31             }else ch[x][i]=ch[fail[x]][i];
32         }
33     }
34 }
35 
36 int d[111][111][MAXN][4];
37 
38 int main(){
39     idx['R']=0; idx['D']=1;
40     int t,n,m;
41     char str[111];
42     scanf("%d",&t);
43     while(t--){
44         tn=0;
45         memset(ch,0,sizeof(ch));
46         memset(flag,0,sizeof(flag));
47         scanf("%d%d",&n,&m);
48         for(int i=0; i<2; ++i){
49             scanf("%s",str);
50             insert(str,i);
51         }
52         getfail();
53         memset(d,0,sizeof(d));
54         d[0][0][0][0]=1;
55         for(int len=0; len<n+m; ++len){
56             for(int i=0; i<=len; ++i){
57                 int j=len-i;
58                 if(j>m || i>n) continue;
59                 for(int s=0; s<=tn; ++s){
60                     for(int k=0; k<4; ++k){
61                         d[i+1][j][ch[s][0]][k|flag[ch[s][0]]]+=d[i][j][s][k];
62                         d[i+1][j][ch[s][0]][k|flag[ch[s][0]]]%=1000000007;
63                         d[i][j+1][ch[s][1]][k|flag[ch[s][1]]]+=d[i][j][s][k];
64                         d[i][j+1][ch[s][1]][k|flag[ch[s][1]]]%=1000000007;
65                     }
66                 }
67             }
68         }
69         int res=0;
70         for(int i=0; i<=tn; ++i){
71             res+=d[n][m][i][3];
72             res%=1000000007;
73         }
74         printf("%d\n",res);
75     }
76     return 0;
77 }

 

转载于:https://www.cnblogs.com/WABoss/p/5387697.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值