好久没写题解了(其实是好久没做题了)。。
这题就是一个缝合怪题目,除了堆砌代码之外没有什么难度,所以我决定写一下关于回文树的东西。
对于长度为 的字符串,回文树是可以在 下构造出来的存储该串所有不同回文子串的数据结构。比较特殊的地方是其有两个根,一个根 表示长度为奇数的串,另一个根 表示长度为偶数的串。
在回文树中,从根到任意节点的路径上的字符构成的字符串表示了该节点存储的回文串的一半(如果长度为奇数,则
出发的边上的字符表示了中间的字符)。
换句话说,设当前节点为
,
表示的回文子串为
,
的一个孩子为
,且边
表示的字符为
,那么
表示的回文子串为
。所以两个根表示的都是空串,为了方便,我们总是将
表示的串的长度设为
。
在继续口胡之前,我们先直接扔出一个规律:一个长度为
的字符串产生的不同的回文子串不会超过
个。
这实际上是因为当我们为一个长度为
的字符串末尾添加一个字符时,新产生的不同的回文子串只可能是包含第
个字符的最长的回文子串。容易证明,更短的回文子串必然已经在
中出现过。
由这个规律,我们便有了构造回文树的方法:当我们处理到第
个字符时,我们寻找
中最长的后缀回文子串,如果这个子串没被处理过,我们便新建节点。同样由这个规律,我们知道回文树的空间也是
的。
至于如何实现,我们只需要为每个节点维护一个指向其最长后缀回文子串的指针即可。每次更新,我们先找到最长后缀回文子串
,新建节点(如果需要的话)之后,再找到第二长的后缀回文子串
,由之前的规律,
必然已经被处理过,我们直接更新指针即可。
最后分析一下这么做的时间复杂度,设
表示字符串
的后缀回文子串个数,容易得到
,因为前者的一个后缀回文子串必然包括了后者的一个后缀回文子串(除了长度为
的回文子串的情况,此时包括了后者的一个空子串,这也是那个多出来的
的来历)。所以实际上每次更新,
的值最多只会增加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;
};