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 }