訓練賽第二場的I題,上完體育課回來就把這題過了,今天訓練賽rank1了,還把大大隊虐了,並且我還過了這道題 (雖然我也就過了這道題。。。),第一次在比賽中手寫AC自動機還帶dp的,心情大好。node
給一個字符串集合,求包含該集合超過K個字符的,長度爲L的字符串的個數。ios
顯然是在AC自動機上跑dp,設dp[u][L][k]表示當前在結點u,還要走L步,當前狀態爲k的個數。一開始第三維表示的是包含k個字符串,可是題目要求不含重複的,那就只能狀壓了。轉移爲dp[u][L][k]+=dp[v][L-1][nk],nk爲k通過u以後的狀態,即直接沿着失配邊往回走,把能配到的都加進來就好了。當cnt(k)>=K是就不用再走了,直接返回26^L。數據結構
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<queue> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=252; const int INF=1e9+10; const ll MOD=20090717LL; ll qpow(ll n,ll k,ll p) { ll res=1; while(k){ if(k&1) res=(res*(n%p))%p; n=((n%p)*(n%p))%p; k>>=1; } return res; } ll Cnt(ll k) { ll res=0; for(ll i=0;i<=10;i++){ if(k&(1<<i)) res++; } return res; } int L,M,K; int dp[252][26][(1<<10)+10]; struct Trie { int ch[maxn][26]; int f[maxn]; int last[maxn]; int End[maxn]; int rt,tot; int newnode() { ++tot; memset(ch[tot],-1,sizeof(ch[tot])); End[tot]=0; return tot; } void init() { tot=-1; rt=newnode(); } void insert(char *s,int ID) { int len=strlen(s),u=rt; REP(i,0,len-1){ int c=s[i]-'a'; if(ch[u][c]==-1) ch[u][c]=newnode(); u=ch[u][c]; } End[u]=(1<<ID); } void build() { queue<int> q; f[rt]=rt;last[rt]=rt; REP(c,0,25){ if(~ch[rt][c]) f[ch[rt][c]]=rt,q.push(ch[rt][c]); else ch[rt][c]=rt; } while(!q.empty()){ int u=q.front();q.pop(); REP(c,0,25){ if(~ch[u][c]) f[ch[u][c]]=ch[f[u]][c],q.push(ch[u][c]); else ch[u][c]=ch[f[u]][c]; } if(End[f[u]]) last[u]=f[u]; else last[u]=last[f[u]]; } } int get(int u) { if(u==rt) return 0; return End[u]|get(last[u]); } int dfs(int u,int L,int k) { int &res=dp[u][L][k]; if(~res) return res; if(Cnt(k)>=K) return res=qpow(26,L,MOD); if(L==0) return res=0; res=0; int nk=0; REP(c,0,25){ int v=ch[u][c]; nk=k; if(End[v]) nk|=get(v); else if(last[v]) nk|=get(last[v]); res=(res+dfs(ch[u][c],L-1,nk))%MOD; } return res; } };Trie ac; char s[maxn]; int main() { while(~scanf("%d%d%d",&L,&M,&K)){ if(L==0&&M==0&&K==0) break; //cout<<"L="<<L<<" M="<<M<<" K="<<K<<endl; ac.init(); REP(i,1,M){ scanf("%s",s); if(strlen(s)>L) continue; ac.insert(s,i-1); } ac.build(); memset(dp,-1,sizeof(dp)); printf("%I64d\n",ac.dfs(ac.rt,L,0)); } return 0; }
第一次在比賽中過數據結構,心情大好。ide
可是仍是有些要總結的:測試
首先仍是提交MLE以後沒有隻是稍稍優化而沒有優化到不能再優化爲止,所以MLE了三次,其實有兩次是不必的,接着沒有算cnt的考慮常數,直接循環到20,致使TLE了,其實直接除以2會更好,常數也小。接着是long long和int的選擇,通常用longlong的惟一風險就是MLE了,碰到MLE應該立刻考慮longlong,或者用以前要先考慮用longlong仍是會int。優化
總之應該提交以前或者寫代碼以前應該除了考慮多種狀況測試邊緣數據防止WA以外,還應該考慮時間空間複雜度,防止MLE和TLE,必要的時候還要優化常數。ui