洛谷CF264D Colorful Stones(子序列匹配,思維)
洛谷題目傳送門
神仙思維題。
對於兩個字符串的匹配問題,似乎之前蒟蒻寫的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)\)也會不可達。對應的兩個子串形如AB
和BA
,蒟蒻接下來把該狀態記作AB-BA。
仔細觀察一下(或者打個表),除了這種情況,還有沒有別的情況也是在界內卻不可達的?貌似找不到啊。。。。。。
實際上,我們大概可以證明,在這個界內有且僅有AB-BA狀態不可達。
圖中的若幹有向邊從前驅節點指向後繼節點。顯然如果一個狀態不可達,那麽要麽它沒有前驅,要麽它的所有前驅都不可達。
首先,一個節點沒有前驅的情況就只有AB-BA那一種。當\(s_x=t_y\)
於是現在我們就需要證明,如果一個點不可達,那麽它一定沒有前驅,而不會出現它有前驅且前驅不可達。反證法,我們現在開始判定一個在界內的有前驅的節點\((x,y)\),並假設它和它的前驅都不可達。
- 它的前驅中有一個是\((x-1,y-1)\)。剛剛已經得出\((x-1,y-1)\)有前驅,那麽我們又需要假設\((x-1,y-1)\)的前驅不可達。
- 它的前驅中沒有\((x-1,y-1)\)。則它的前驅可能有\((x-1,y)\)、\((x,y-1)\)。如果\((x-1,y)\)有前驅,那麽我們又需要假設\((x-1,y)\)的前驅不可達;如果\((x-1,y)\)
於是,我們如果要假設某個點的所有前驅都不可達,我們必須假設它的某一個前驅的所有前驅都不可達,接著是前驅的前驅的前驅。。。。。。這個過程中\(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(子序列匹配,思維)