題解 P7861 [COCI2015-2016#2] SAVEZ
阿新 • • 發佈:2021-10-20
Trie優化dp
,把每個字串的二元組當成一個字符集為 \(26\times26\) 的字串插入,在插入的過程中如果當前節點是某個字串的結尾就直接進行 \(\mathsf{dp}\),並在插入完時的結點記錄此字串編號即可。
模擬賽原題,賽時一直在肝另一題就沒做
Solution
求最長子序列的題,顯然可以 \(\mathsf{dp}\),方程為 \(f_i=\max\limits_{x_j\,為\,x_i\,的字首和字尾}{f_j}+1\)。
考慮怎麼判斷一個字串為另一個字串的的字首和字尾。我們有一個很妙的做法:把字串 \(s_{1\cdots n}\) 正反合在一起組成 \(n\) 個字元二元組,即 \((s_1,s_n)(s_2,s_{n-1})\dots(s_n,s_1)\),那麼這樣就只用判斷 \(x_j\) 組成的二元組是否是 \(x_i\) 的二元組的字首就好了。
對於這些二元組,我們可以直接建 \(\mathsf{Trie}\)
關於實現,由於這棵 \(\mathsf{Trie}\) 的每條邊有兩個字元 \(\texttt{ab}\),我們可以把它轉化為一個數 \(\texttt{(a-'A')*26+(b-'A')}\),並用 \(\mathsf{unordered\_map}\) 實現動態開點的子節點列表。
Code
#include<bits/stdc++.h> using namespace std; const int N=2e6+5; int n,m,ans,tot,f[N],id[N];char s[N]; unordered_map<int,unordered_map<int,int>>g;//Trie int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s+1);m=strlen(s+1); int u=0; for(int j=1;j<=m;j++){ f[i]=max(f[i],f[id[u]]+1); int w=(s[j]-'A')*26+s[m-j+1]-'A';//正反合並 if(g[u].find(w)==g[u].end())g[u][w]=++tot;//動態開點 u=g[u][w]; }if(id[u])f[i]=max(f[i],f[id[u]]+1); ans=max(ans,f[i]);id[u]=i; }printf("%d\n",ans); return 0; }
最後祝各位 \(\texttt{CSP2021 rp++!}\)