1. 程式人生 > >洛谷CF264D Colorful Stones(子序列匹配,思維)

洛谷CF264D Colorful Stones(子序列匹配,思維)

兩個 www. == mes 一個點 tps .com 狀態 圖模型

洛谷題目傳送門

神仙思維題。

對於兩個字符串的匹配問題,似乎之前蒟蒻寫的HAOI2010最長公共子序列題解中提到的建網格圖模型是一種套路?

給一個稍微強一點的樣例(把字母換成了ABC)

AABCB
BACBA

它所對應的網格圖如下(橫軸代表\(s\),縱軸代表\(t\),顯示的點表示可達狀態)

技術分享圖片

我們首先可以大致確定,所有的可達狀態在一個不規則圖形的界內
(紅色線條)。第\(i\)行(或列)的界是\([l_i,r_i]\),而且類似two pointers,\(l_i\)\(r_i\)都隨\(i\)單調不降。拐角的頂點\((x,y)\)出現在前綴\(s_x\)和前綴\(t_y\)第一次匹配到其中一個是另一個的子序列的地方。

那麽是不是這個界裏面的狀態都可達呢?顯然不是,我們還可以看到這樣的位置(中間有三個):如果\(s_x=t_{y-1}\neq s_{x-1}=t_y\)的話,\((x,y)\)也會不可達。對應的兩個子串形如ABBA,蒟蒻接下來把該狀態記作AB-BA。

仔細觀察一下(或者打個表),除了這種情況,還有沒有別的情況也是在界內卻不可達的?貌似找不到啊。。。。。。

實際上,我們大概可以證明,在這個界內有且僅有AB-BA狀態不可達。

圖中的若幹有向邊從前驅節點指向後繼節點。顯然如果一個狀態不可達,那麽要麽它沒有前驅,要麽它的所有前驅都不可達。

首先,一個節點沒有前驅的情況就只有AB-BA那一種。當\(s_x=t_y\)

時,我們可以肯定\((x,y)\)有前驅,隨手畫畫就可以發現。

於是現在我們就需要證明,如果一個點不可達,那麽它一定沒有前驅,而不會出現它有前驅且前驅不可達。反證法,我們現在開始判定一個在界內的有前驅的節點\((x,y)\),並假設它和它的前驅都不可達。

  1. 它的前驅中有一個是\((x-1,y-1)\)。剛剛已經得出\((x-1,y-1)\)有前驅,那麽我們又需要假設\((x-1,y-1)\)的前驅不可達。
  2. 它的前驅中沒有\((x-1,y-1)\)。則它的前驅可能有\((x-1,y)\)\((x,y-1)\)。如果\((x-1,y)\)有前驅,那麽我們又需要假設\((x-1,y)\)的前驅不可達;如果\((x-1,y)\)
    沒有前驅,那麽說明出現了AB-BA狀態,則一定會有\((x-1,y-1)\)\((x,y)\)的邊,不符合設定。對\((x,y-1)\)的討論同理。

於是,我們如果要假設某個點的所有前驅都不可達,我們必須假設它的某一個前驅的所有前驅都不可達,接著是前驅的前驅的前驅。。。。。。這個過程中\(x,y\)在遞減,而最終\((x,y)\)到了邊界上。顯然邊界上的點都是可達狀態(從\((0,0)\)出發形成一條輪廓狀路徑),於是所有的假設都被推翻了。

思路清晰了以後,代碼就簡單了,只需要註意些細節。動態匹配子序列,維護\(l,r\),還有對不同的狀態記前綴和,這些都沒什麽好說的了。

#include<bits/stdc++.h>
#define RG register
#define R RG int
using namespace std;
const int N=1e6+9;
char s[N],t[N];
int f[N][8];
int main(){
    R n=0,m=0,x,y,l=0,r=0;
    RG long long ans=0;
    scanf("%s%s",s,t);
    for(n=0;s[n];++n)s[n]%=3;//只是湊巧發現RBG%3的余數不一樣
    for(m=0;t[m];++m)t[m]%=3;
    for(x=1;x<n;++x){
        memcpy(f[x],f[x-1],32);//前綴和
        if(s[x-1]!=s[x])
            ++f[x][(s[x-1]>s[x])*4+s[x-1]+s[x]];
    }
    memcpy(f[n],f[n-1],32);
    for(y=0;y<m;++y){
        if(y&&t[y-1]!=t[y]){//註意邊界
            x=(t[y-1]<t[y])*4+t[y-1]+t[y];
            ans-=f[r][x]-f[l][x];
        }
        while(r<n&&s[r]!=t[y])++r;
        ans+=r-l+1-(r==n);//同樣註意邊界
        if(r<n)++r;
        if(l<r&&s[l]==t[y])++l;
    }
    cout<<ans<<endl;
    return 0;
}

洛谷CF264D Colorful Stones(子序列匹配,思維)