[XSY] 相似(DP套DP)
生活随笔
收集整理的這篇文章主要介紹了
[XSY] 相似(DP套DP)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
相似
在看這道題前,有必要先看一下DP套DP的入門題[uoj3864]Hero meet devil,附上兩篇寫得不錯的題解:
https://blog.csdn.net/Ike940067893/article/details/87863041
https://www.cnblogs.com/RabbitHu/p/BZOJ3864.html
- 可以發現,SSS和TTT相似,等價于它們的最長公共子序列長度至少為n?kn-kn?k
- 考慮如何求兩個字符串的LCS。設 lcsi,jlcs_{i,j}lcsi,j? 表示 S[1...i]S[1...i]S[1...i] 與 T[1...j]T[1...j]T[1...j] 的 LCS 長度,轉移顯然為
lcsi,j={lcsi?1,j?1+1(S[i]==T[j])max{lcsi?1,j,lcsi,j?1}(S[i]!=T[j])lcs_{i,j}=\left\{ \begin{aligned} &lcs_{i-1,j-1}+1(S[i]==T[j]) \\ & max\{lcs_{i-1,j},lcs_{i,j-1}\} (S[i]!=T[j])\\ \end{aligned} \right. lcsi,j?={?lcsi?1,j?1?+1(S[i]==T[j])max{lcsi?1,j?,lcsi,j?1?}(S[i]!=T[j])? - 考慮如何拓展到TTT任意的情況。自然的想法是DP套DP:設dp[j][lcs1,j,lcs2,j,...,lcsn,j]dp[j][lcs_{1,j},lcs_{2,j},...,lcs_{n,j}]dp[j][lcs1,j?,lcs2,j?,...,lcsn,j?]表示 考慮到TTT的前jjj位,lcs?,jlcs_{-,j}lcs?,j?的情況如第二維所示 的方案數
- 直接這樣維護狀態肯定是行不通的,考慮優化:
1.發現 lcsi,j?lcsi?1,j∈{0,1}lcs_{i,j}-lcs_{i-1,j}\in\{0,1\}lcsi,j??lcsi?1,j?∈{0,1},那么 lcs1,j,lcs2,j,...,lcsn,jlcs_{1,j},lcs_{2,j},...,lcs_{n,j}lcs1,j?,lcs2,j?,...,lcsn,j?的差分數組必是一個01 串 ,我們 狀壓這個01串 來描述第二維的狀態
2.nnn位01串還是太大了,考慮如何利用kkk很小這個性質進行優化。
考慮某個lcsi,j,若∣i?j∣>k,那么在轉移到lcsn,n時,必然值不超過lcsi,j+min{n?i,n?j}<=min{i,j}+min{n?i,n?j}=n?∣i?j∣<n?k\color{Red}{考慮某個lcs_{i,j}},若|i-j|>k,那么在轉移到lcs_{n,n}時,必然值不超過lcs_{i,j}+min\{n-i,n-j\}<=min\{i,j\}+min\{n-i,n-j\}=n-|i-j|<n-k考慮某個lcsi,j?,若∣i?j∣>k,那么在轉移到lcsn,n?時,必然值不超過lcsi,j?+min{n?i,n?j}<=min{i,j}+min{n?i,n?j}=n?∣i?j∣<n?k
因此這樣的lcsi,jlcs_{i,j}lcsi,j?是沒用的,可以直接丟掉,即對于每個jjj,我們只需要記錄lcsj?k,j,lcsj?k+1,j,...,lcsj+k,jlcs_{j-k,j},lcs_{j-k+1,j},...,lcs_{j+k,j}lcsj?k,j?,lcsj?k+1,j?,...,lcsj+k,j?
3.只知道lcsj?k,jlcs_{j-k,j}lcsj?k,j?~lcsj+k,jlcs_{j+k,j}lcsj+k,j?的差分數組,我們是無法知道它們的實際值的,因此考慮開多一維來記錄lcsj,jlcs_{j,j}lcsj,j?的實際值以推出其它的lcslcslcs值,即我們設dp[j][x][s]dp[j][x][s]dp[j][x][s]表示 考慮到TTT的前jjj位,lcsj,j==xlcs_{j,j}==xlcsj,j?==x,lcsj?k,j,lcsj?k+1,j,...,lcsj+k,jlcs_{j-k,j},lcs_{j-k+1,j},...,lcs_{j+k,j}lcsj?k,j?,lcsj?k+1,j?,...,lcsj+k,j?的差分數組狀壓后為sss 的方案數
4.∵lcsn,n∈[n?k,n](即不匹配的字符最多k個)\because lcs_{n,n}\in [n-k,n](即不匹配的字符最多k個)∵lcsn,n?∈[n?k,n](即不匹配的字符最多k個)
∴?j∈[1,n]滿足lcsj,j∈[j?k,j]\therefore\forall j\in[1,n]滿足lcs_{j,j}\in [j-k,j]∴?j∈[1,n]滿足lcsj,j?∈[j?k,j],所以我們可以將dpdpdp的第二維設置為lcsj,j?(j?k)=lcsj,j?j+klcs_{j,j}-(j-k)=lcs_{j,j}-j+klcsj,j??(j?k)=lcsj,j??j+k,即dp[][x][]dp[][x][]dp[][x][]表示lcsj,j?j+k==xlcs_{j,j}-j+k==xlcsj,j??j+k==x,這樣就令x∈[0,k]x\in[0,k]x∈[0,k] - 考慮如何實現狀態轉移。dpdpdp數組的第三維不好直接得到,我們考慮把它預處理出來:設p[i][j][s1][c]p[i][j][s1][c]p[i][j][s1][c]表示考慮到TTT的前iii位,lcsi,i==jlcs_{i,i}==jlcsi,i?==j(知道lcsi,ilcs_{i,i}lcsi,i?就可以通過差分數組推出其它的lcslcslcs值),lcsi?k,i,lcsi?k+1,i,...,lcsi+k,ilcs_{i-k,i},lcs_{i-k+1,i},...,lcs_{i+k,i}lcsi?k,i?,lcsi?k+1,i?,...,lcsi+k,i?的差分數組狀壓后為s1,在TTT的i+1i+1i+1位新增字符ccc后,lcsi+1?k,i+1,lcsi+2?k,i+1,...,lcsi+1+k,i+1lcs_{i+1-k,i+1},lcs_{i+2-k,i+1},...,lcs_{i+1+k,i+1}lcsi+1?k,i+1?,lcsi+2?k,i+1?,...,lcsi+1+k,i+1?狀壓完的差分數組
- 同樣,需要優化ppp的狀態設置:
1.同上,我們可以將ppp的第二維設置為lcsi,i?(i?k)=lcsi,i?i+klcs_{i,i}-(i-k)=lcs_{i,i}-i+klcsi,i??(i?k)=lcsi,i??i+k,即p[][j][][]p[][j][][]p[][j][][]表示lcsi,i?i+k==jlcs_{i,i}-i+k==jlcsi,i??i+k==j,這樣就令j∈[0,k]j\in[0,k]j∈[0,k]
2.其實ppp轉移到的新差分數組狀態與新增字符具體是什么無關,與新增字符與S每一位的匹配情況有關。再進一步地,
∵lcsh,h∈[h?k,h]\because lcs_{h,h}\in [h-k,h]∵lcsh,h?∈[h?k,h]
∴T[h]只可能與S[h?k]\therefore T[h]只可能與 S[h-k]∴T[h]只可能與S[h?k]~S[h+k]匹配S[h+k] 匹配S[h+k]匹配,即我們只需要考慮新增字符與S[i+1?k]S[i+1-k]S[i+1?k]~S[i+1+k]S[i+1+k]S[i+1+k]的匹配情況。直接把第四維設置成這個匹配情況即可。
3.仔細想想,把第四維改成匹配情況后,ppp的轉移已經與第一維無關了,直接刪掉即可
至此,這道題就可以實現了
#include<iostream> #include<cstdio> #include<cstring> #define mod 998244353 using namespace std; typedef pair<int,int> pr; const int N=30001; const int K=5; const int S=256; int n,m,ans,dp[N][K][S],f[31],g[31]; pr p[K][S][S<<1]; char ch[N]; bool flag[31]; int add(int a,int b){a=a+b;if(a>mod) a-=mod;return a; } int mul(int a,int b){return 1ll*a*b%mod; } void dfs(int a,int b,int c){if(dp[a][b][c]==0) return;int l=max(a-m,0),r=min(a+m,n-1),sz=26;for(int i=l;i<=r;i++){if(!flag[ch[i+1]-'A']){flag[ch[i+1]-'A']=1;sz--;}f[ch[i+1]-'A']|=(1<<(i-a+m));//統計每種字符的匹配情況 }for(int i=l;i<=r;i++){if(flag[ch[i+1]-'A']){int x=p[b][c][f[ch[i+1]-'A']].first;int y=p[b][c][f[ch[i+1]-'A']].second;if(x>=0) dp[a+1][x][y]=add(dp[a+1][x][y],dp[a][b][c]);flag[ch[i+1]-'A']=0;f[ch[i+1]-'A']=0;}}int x=p[b][c][0].first,y=p[b][c][0].second;if(x>=0) dp[a+1][x][y]=add(dp[a+1][x][y],mul(dp[a][b][c],sz)); } int main(){scanf("%s%d",ch+1,&m);n=strlen(ch+1);//省略一維l,表示考慮到第l位 for(int i=0;i<=m;i++){//lcs[l][l]-l+m==i for(int j=0;j<(1<<(2*m));j++){//lcs[l-m][l]~lcs[l+m][l]的差分數組狀壓 for(int k=0;k<(1<<(2*m+1));k++){//T[l+1]與S[l+1-m]~S[l+1+m]匹配情況狀壓 //f:(lcs[l-m][l]-l+m)~(lcs[l+m][l]-l+m) //g:(lcs[l+1-m][l+1]-l+m)~(lcs[l+1+m][l+1]-l+m)f[m]=i;for(int a=m-1;a>=0;a--) f[a]=f[a+1]-(j>>a&1);for(int a=m;a<2*m;a++) f[a+1]=f[a]+(j>>a&1);for(int a=0;a<=2*m;a++){if((k>>a)&1) g[a+1]=max(f[a]+1,f[a+1]);//匹配else g[a+1]=max(f[a],f[a+1]); //不匹配 g[a+1]=max(g[a],g[a+1]);}int x=g[m+1]-1;//x=lcs[l+1][l+1]-(l+1)+mint y=0;if(x>=0){for(int a=m-1;a>=0;a--) if(g[a+1]^g[a+2]) y|=1<<a;for(int a=m;a<2*m;a++) if(g[a+1]^g[a+2]) y|=1<<a;}p[i][j][k]=make_pair(x,y);}}}//預處理 for(int i=0;i<=30;i++) f[i]=0;dp[0][m][0]=1;for(int i=0;i<n;i++){//考慮到第i位 for(int j=0;j<=m;j++){//lcs[i][i]-i+m==j for(int k=0;k<(1<<(2*m));k++){//差分數組狀壓 dfs(i,j,k);}}}//dpfor(int i=0;i<=m;i++){for(int j=0;j<(1<<(2*m));j++){ans=add(ans,dp[n][i][j]);}}printf("%d\n",ans);return 0; }總結
以上是生活随笔為你收集整理的[XSY] 相似(DP套DP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [XSY] 分割(dfs树)
- 下一篇: 塑料落到木地板上我说了句我爱你什么意思