洛谷P2516 [HAOI2010]最長公共子序列(LCS,最短路)

洛谷題目傳送門c++

一進來就看到一個多月前秒了此題的ysn和YCB%%%數組

最長公共子序列的\(O(n^2)\)的求解,Dalao們想必都很熟悉了吧!不過蒟蒻忽然發現,用網格圖貌似能夠很輕鬆地理解這個東東?優化

設字符串長度爲\(n,m\),那麼想象咱們有一個\(n+1\)\(m+1\)列的網格圖,只能從左下角往右、上兩個方向走。定義每條路徑的長度都爲\(1\)。記第\(i\)行第\(j\)列爲\((i,j)\)spa

話說網格圖真tm難畫code

求最長公共子序列本質上是在兩個序列中尋找最多的配對,並且這些配對的位置在序列中的位置也要分別遞增。blog

那麼,若是\(x_i\)\(y_j\)相等,那麼咱們就從\((i-1,j-1)\)\((i,j)\)連一條邊。這在網格圖中分明是一條條捷徑,那麼咱們要尋找最長公共子序列,可不能夠轉化爲尋找最短路,或者說尋找通過捷徑次數最多的路徑呢?字符串

這個模型是很巧妙的,知足了配對的位置在序列中的位置分別遞增(由於只能往右、上走)。get

那麼再看第二問。顯然在這個模型中,不一樣的公共子序列對應的,不是至少有一條邊不相同的路徑,而是至少有一條捷徑不相同的路徑。那麼這個該怎麼DP呢?it

設到達\((i,j)\)最多能通過的捷徑數(即序列的兩個前綴的最長公共子序列長度)爲\(mf_{i,j}\),方案數爲\(f_{i,j}\)。顯然\((i,j)\)能夠從\((i-1,j)\)\((i,j-1)\)轉移,若是\(x_i=y_j\)那麼還能夠從\((i-1,j-1)\)轉移(\(mf\)加上\(1\))。依次轉移,若是新的\(mf\)更大則直接覆蓋原信息,若是\(mf\)相等則\(f\)相加。class

然而,再次注意不一樣路徑的定義。那麼是否是可能存在這樣一種狀況:到\((i-1,j-1)\)的一條路徑,分別轉移給了\((i-1,j)\)\((i,j-1)\),而再一次轉移給了\((i,j)\),沒有通過不一樣的捷徑,卻計算了兩遍!顯然只有\(mf_{i-1,j-1}=mf_{i,j}\)的時候上述狀況纔會發生,那麼這時咱們從\(f_{i,j}\)減去\(f_{i-1,j-1}\)便可。

思路都清晰了。在開始碼DP以前,咱們還須要注意這個DP的過程,每行只會從上一行轉移,因而使用滾動數組優化空間,防止MLE。

#include<bits/stdc++.h>
#define RG register
#define I inline
#define R RG int
#define G c=getchar()
using namespace std;
typedef long long LL;
const int N=5009,YL=1e8;
char x[N],y[N];
int ff[N],gg[N],mff[N],mgg[N];
int main(){
    scanf("%s%s",x+1,y+1);
    R n=strlen(x+1)-1,m=strlen(y+1)-1,i,j,*f=ff,*g=gg,*mf=mff,*mg=mgg;
    g[0]=1;for(j=0;j<=m;++j)f[j]=1;
    for(i=1;i<=n;++i,swap(f,g),swap(mf,mg)){//滾動數組
        memset(g +1,0,m<<2);//注意清空
        memset(mg+1,0,m<<2);
        for(j=1;j<=m;++j){//三方向轉移
            if(x[i]==y[j])mg[j]=mf[j-1]+1,g[j]=f[j-1];
            if(mf[j]>mg[j])mg[j]=mf[j],g[j]=f[j];//覆蓋
            else if(mf[j]==mg[j])(g[j]+=f[j])%=YL;//相加
            if(mg[j-1]>mg[j])mg[j]=mg[j-1],g[j]=g[j-1];
            else if(mg[j-1]==mg[j])(g[j]+=g[j-1])%=YL;
            if(mf[j-1]==mg[j])(g[j]+=YL-f[j-1])%=YL;//減掉重複的部分
        }
    }
    printf("%d\n%d\n",mf[m],f[m]);
    return 0;
}
相關文章
相關標籤/搜索