第十五屆浙江大學寧波理工學院程式設計大賽(同步賽)LCPS
好久沒寫題解了(其實是好久沒做題了)。。
這題就是一個縫合怪題目,除了堆砌程式碼之外沒有什麼難度,所以我決定寫一下關於迴文樹的東西。
對於長度為 的字串,迴文樹是可以在 下構造出來的儲存該串所有不同迴文子串的資料結構。比較特殊的地方是其有兩個根,一個根 表示長度為奇數的串,另一個根 表示長度為偶數的串。
在迴文樹中,從根到任意節點的路徑上的字元構成的字串表示了該節點儲存的迴文串的一半(如果長度為奇數,則
出發的邊上的字元表示了中間的字元)。
換句話說,設當前節點為
,
表示的迴文子串為
,
的一個孩子為
,且邊
表示的字元為
,那麼
表示的迴文子串為
。所以兩個根表示的都是空串,為了方便,我們總是將
表示的串的長度設為
。
在繼續口胡之前,我們先直接扔出一個規律:一個長度為
的字串產生的不同的迴文子串不會超過
個。
這實際上是因為當我們為一個長度為
的字串末尾新增一個字元時,新產生的不同的迴文子串只可能是包含第
個字元的最長的迴文子串。容易證明,更短的迴文子串必然已經在
中出現過。
由這個規律,我們便有了構造迴文樹的方法:當我們處理到第
個字元時,我們尋找
中最長的字尾迴文子串,如果這個子串沒被處理過,我們便新建節點。同樣由這個規律,我們知道迴文樹的空間也是
的。
至於如何實現,我們只需要為每個節點維護一個指向其最長字尾迴文子串的指標即可。每次更新,我們先找到最長字尾迴文子串
,新建節點(如果需要的話)之後,再找到第二長的字尾迴文子串
,由之前的規律,
必然已經被處理過,我們直接更新指標即可。
最後分析一下這麼做的時間複雜度,設
表示字串
的字尾迴文子串個數,容易得到
,因為前者的一個字尾迴文子串必然包括了後者的一個字尾迴文子串(除了長度為
的迴文子串的情況,此時包括了後者的一個空子串,這也是那個多出來的
的來歷)。所以實際上每次更新,
的值最多隻會增加1,所以對一個長度為
的字串構造一棵迴文樹的時間複雜度為
。
回到題目,只需要構造出兩個字串的迴文樹,取其交集,然後從葉子開始bfs找出最小字典序的迴文串,然後在原串中找到第一個匹配點即可。
不知道這種縫合怪題目有什麼意義。。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<climits>
#include<cstdio>
#include<random>
using namespace std;
//--Container
#include<queue>
typedef pair<int,int> pi;
//--
#define clr(a) memset(a,0,sizeof(a))
typedef long long ll;
const int up=2000000;
int n,nx[2][up+10][26],px[2][up+10],le[2][up+10],mx[up+10];char cz[2][up+10];
void _cl(int id){
int i,j,t,tn,lst;clr(nx[id][0]),clr(nx[id][1]),px[id][1]=0,le[id][0]=-1,le[id][1]=0;
for(tn=i=1,lst=0;i<=n;++i){
for(t=cz[id][i]-'a',j=lst;cz[id][i]!=cz[id][i-le[id][j]-1];j=px[id][j]);
if(nx[id][j][t])lst=nx[id][j][t];
else{
lst=++tn;clr(nx[id][tn]),le[id][tn]=le[id][j]+2,nx[id][j][t]=tn;
for(j=px[id][j];cz[id][i]!=cz[id][i-le[id][j]-1];j=px[id][j]);
px[id][tn]=le[id][tn]>1?nx[id][j][t]:1;
}
}
};
int dfs(int v,int u){
int i,j,x=le[0][v];for(i=0;i<26;++i){
if(nx[0][v][i]&&nx[1][u][i])x=max(x,dfs(nx[0][v][i],nx[1][u][i]));
else
nx[0][v][i]=0;
}
return mx[v]=x;
};
char ds[up+10],rs[up+10],fq[up+10];int dn,fqq[up+10],fdn[2],fn[2],fuck[2][up+10];bool bd[up+10];
void genpx(int n){
int i,j;for(fqq[0]=fqq[1]=0,i=2;i<=n;++i){
for(j=fqq[i-1];j&&rs[j+1]!=rs[i];j=fqq[j]);
fqq[i]=rs[j+1]==rs[i]?j+1:0;
}
};
int kksk(int x,char*ar){
int i,j,t;for(i=j=1;;++i,++j){
if(j==x&&rs[j]==ar[i])return i-x+1;
if(rs[j]!=ar[i]){
for(t=fqq[j-1];t&&rs[t+1]!=ar[i];t=fqq[t]);
j=rs[t+1]==ar[i]?t+1:0;
}
}
return 19260817;
};
void SBChuTiRenKaNiMaDe_log(int x,int&ra,int&rb){
int i,j,k,d,t;genpx(x);
ra=kksk(x,cz[0]),rb=kksk(x,cz[1]);
};
void dfx(int v,int x){
int i,j;if(le[0][v]==x)fuck[0][fn[0]++]=v;
for(i=0;i<26;++i)if(nx[0][v][i]&&mx[nx[0][v][i]]==x)dfx(nx[0][v][i],x),fqq[nx[0][v][i]]=v,fq[nx[0][v][i]]=i+'a';
};
void shitfs(int x){
int i,j,k,d,a,b,t;for(i=0;i<=n;bd[i++]=0);fn[0]=fn[1]=0;
dfx(x&1?0:1,x);for(fdn[a=0]=x/2+(x&1);;a^=1){
if(!fdn[a])break;char z='z';for(i=0;i<fn[a];z=min(z,fq[fuck[a][i++]]));ds[fdn[a]]=z;
for(fdn[a^1]=fdn[a]-1,fn[a^1]=0,i=0;i<fn[a];++i)if(fq[fuck[a][i]]==z&&!bd[fuck[a][i]]){
fuck[a^1][fn[a^1]++]=fqq[fuck[a][i]];bd[fuck[a][i]]=1;
}
}
};
void cl(){
int i,j,k,d,t;scanf("%d",&n);scanf("%s",cz[0]+1);scanf("%s",cz[1]+1);
_cl(0),_cl(1);t=max(dfs(0,0),dfs(1,1));
printf("%d\n",t);if(t){
dn=t/2+(t&1);shitfs(t);
for(i=t,j=dn;j;--i,--j)rs[i]=ds[j];
for(i=1,j=dn;j;++i,--j)rs[i]=ds[j];
rs[t+1]=0;SBChuTiRenKaNiMaDe_log(t,i,j);
printf("%d %d\n%d %d\n",i,i+t-1,j,j+t-1);
}
};
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif // ONLINE_JUDGE
int t;scanf("%d",&t);while(t--)cl();
return 0;
};