AOJ 2257 Sakura Poetry
SRM Div1 medを解いていたら,解法が浮かんだ.
ローカルで通すのは簡単だけど,AOJで通すにはメモリ管理が必要.
問題文
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2257
解法
DP + Aho-corasick
季語の集合をAho-corasickを使って,オートマトンを作成
dpテーブルは,dp[文字列の長さ][単語][季語を使ったかどうか][オートマトンの状態]とする.
オートマトンの状態数の上限は季語の集合の全文字数より,30*20=600通り
単語数の上限は接続辞書の単語数より,250*2=500通り
単語の遷移の上限は500*500ではなく,接続辞書の情報数の上限250通り
よって,500*250*600*2 =1.5*10^9になるが,ローカルで解ける.また,dpテーブルはスパースなのでAOJでも間に合う.
時間計算量はO(MNK)
AOJのメモリ制限では,dp[501][500][601][2]という配列を用意すると,MLE
しかし,このdpテーブルはスパースなので,mapなどを使うことでMLEを回避できる.
ソースコードは以下の通り
#include <bits/stdc++.h> #define range(i,a,b) for(int i=a;i<b;++i) #define rep(i,n) range(i,0,n) using namespace std; typedef long long ll; int n,m,k; string words[510]; string from[255],to[255]; string season[35]; int fg[610][27]; int ac[610]; int build(){ memset(fg,0,sizeof(fg)); memset(ac,0,sizeof(ac)); int root=1,size=2; fg[root][0]=root; rep(i,k){ int cur=root; rep(j,season[i].size()){ int tar=season[i][j]-'a'+1; if(fg[cur][tar]==0) fg[cur][tar]=size++; cur=fg[cur][tar]; } ac[cur]|=(1<<i); } queue<int> q; range(i,1,27){ if(fg[root][i]){ fg[fg[root][i]][0]=root; int tar=fg[root][i]; q.push(tar); }else fg[root][i]=root; } while(!q.empty()){ int now=q.front();q.pop(); range(i,1,27){ if(fg[now][i]){ int tar=fg[now][0]; while(!fg[tar][i]) tar=fg[tar][0]; fg[fg[now][i]][0]=fg[tar][i]; ac[fg[now][i]]|=ac[fg[tar][i]]; q.push(fg[now][i]); } } } return size; } const ll mod=1000000007; map<int,int> dp[510][510][2]; // len words ac state int main(void){ ios::sync_with_stdio(false); while(cin >> n >> m >> k){ if(n==0) break; rep(i,n) cin >> from[i] >> to[i]; rep(i,n) words[2*i]=from[i]; rep(i,n) words[2*i+1]=to[i]; sort(words,words+2*n); int w=unique(words,words+2*n)-words; rep(i,k) cin >> season[i]; int root=1,s=build(); vector<int> graph[510]; rep(i,n){ int fi=lower_bound(words,words+w,from[i])-words; int ti=lower_bound(words,words+w,to[i])-words; graph[fi].emplace_back(ti); } rep(i,w) graph[w].emplace_back(i); dp[0][w][0][root]=1LL; rep(i,m){ rep(j,w+1)range(k,root,s)rep(l,2){ if(i>0&&j==w) continue; if(dp[i][j][l].find(k)==dp[i][j][l].end()) continue; for(auto &nj:graph[j]){ int ni=i+words[nj].size(); if(ni>m) continue; int nk=k,nl=l; rep(a,words[nj].size()){ int tar=words[nj][a]-'a'+1; while(!fg[nk][tar]) nk=fg[nk][0]; nk=fg[nk][tar]; nl+=__builtin_popcount(ac[nk]); if(nl>=2) break; } if(nl>=2) continue; dp[ni][nj][nl][nk]+=dp[i][j][l][k]; dp[ni][nj][nl][nk]%=mod; } } rep(j,w+1)rep(l,2) dp[i][j][l].clear(); } int ans=0LL; rep(j,w+1)for(auto &i:dp[m][j][1]){ ans+=i.second; ans%=mod; } cout << ans << endl; rep(j,w+1)rep(l,2) dp[m][j][l].clear(); } return 0; }