LINK

给出一个串 s s s,找出两个不相交的子串 a , b a,b a,b( a a a在前 b b b在后)

满足 a + b a+b a+b是一个回文串


考虑串 ∣ a ∣ > = ∣ b ∣ |a|>=|b| a>=b怎么计算,因为如果 ∣ a ∣ < ∣ b ∣ |a|<|b| a<b,只需要对反串也算一遍就好了

那么一定是 a = b + w a=b+w a=b+w的形式, w w w是一个回文串

我们从回文串下手,枚举每个点 i i i作为回文中心,最长的回文是 [ l , r ] [l,r] [l,r]

那么让 [ l , r ] [l,r] [l,r]作为 w w w,考虑找到一个最小的 i i i满足 [ i , l − 1 ] [i,l-1] [i,l1] [ r + 1 , n ] [r+1,n] [r+1,n]反串的一个子串

正确性

如果不是最长的回文,比如变成 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r1],那么最佳情况下 a a a串长度减一, b b b串长度加一,总长度不变

如果回文变成 [ l + 1 , r ] [l+1,r] [l+1,r] [ l , r − 1 ] [l,r-1] [l,r1]也不行,讨论一下就好了

考虑如何求最小的 i i i满足 [ i , l − 1 ] [i,l-1] [i,l1] [ r + 1 , n ] [r+1,n] [r+1,n]的子串

r e v ( S ) rev(S) rev(S)建立 S A M SAM SAM,拿 S S S的每个前缀放在 S A M SAM SAM上匹配记节点为 p o s [ i ] pos[i] pos[i]

我们对 S A M SAM SAM每个节点维护一个 m i i mi_i mii表示最小的 e n d p o s endpos endpos

这样我们就可以快速知道这个节点是否出现在 S S S [ r + 1 , n ] [r+1,n] [r+1,n]中了

因为在反串中出现最早,所以在原串 S S S中最靠右

那么每次只需要把 p o s [ l − 1 ] pos[l-1] pos[l1]向上跳,因为 m i mi mi值父节点必定小于子节点,所以可以倍增快速跳

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
const int maxn = 4e5+10;
typedef pair<int,int>mp;
mp p1,p2;
int ans,n;
char a[maxn];
int zi[maxn][27],fa[maxn],id,las,l[maxn];
int ed[maxn],mi[maxn],st[maxn][22];
vector<int>vec[maxn];
void insert(int c,int index)
{
	int p = las, np = ++id; las = id;
	l[np] = l[p]+1; mi[np] = ed[np] = index;//最早出现的节点 
	for( ; p&&zi[p][c]==0 ;p=fa[p] )	zi[p][c] = np;
	if( p==0 )	fa[np] = 1;
	else
	{
		int q = zi[p][c];
		if( l[q]==l[p]+1 )	fa[np] = q;
		else
		{
			int nq = ++id;
			memcpy( zi[nq],zi[q],sizeof zi[nq] );
			l[nq] = l[p]+1, fa[nq] = fa[q]; mi[nq] = mi[q], ed[nq] = ed[q];
			fa[np] = fa[q] = nq;
			for( ; p&&zi[p][c]==q;p=fa[p] )	zi[p][c] = nq;
		}
	}
}
void dfs(int u)
{
	st[u][0] = fa[u];
	for(int i=1;i<=20;i++)	st[u][i] = st[st[u][i-1]][i-1];
	for( auto v:vec[u] )
	{
		dfs(v);
		mi[u] = min( mi[u],mi[v] );
		ed[u] = max( ed[u],ed[v] );
	}
}
int pos[maxn],posl[maxn];//分别表示原串中每个前缀在自动机上的位置,和匹配长度 
char P[maxn];
int pal[maxn];


void init()
{
	memset( st,0,sizeof st );
	for(int i=0;i<=id;i++)
	{
		vec[i].clear();
		memset( zi[i],0,sizeof zi[i] );
		fa[i] = 0;
	}
	for(int i=0;i<=n+n+1;i++)
		pos[i] = posl[i] = pal[i] = 0;
	id = las = 1;
}
void work()
{
	init();
	for(int i=1;i<=n;i++)	insert( a[i]-'a',i );
	for(int i=2;i<=id;i++)	vec[fa[i]].push_back( i );
	dfs(1);
	for(int i=n,p=1,le=0;i>=1;i--)
	{
		int c = a[i]-'a';
		if( zi[p][c] )	p = zi[p][c],le++;
		else
		{
			while( p&&zi[p][c]==0 )	p = fa[p];
			if( !p )	p = 1,le = 0;
			else	le = l[p]+1, p = zi[p][c];	
		}
		pos[i] = p;	 posl[i] = le;
	}
	
	int C=0,R=0,t=0;P[++t]='#';
	for(int i=1;i<=n;i++) P[++t]=a[i],P[++t]='#';
	for(int i=1;i<=t;i++)
	{
		if(i<=C+R) pal[i]=min(pal[2*C-i],C+R-i);else pal[i]=0;
		while(i+pal[i]+1<=t&&i-pal[i]-1>=1&&
			  P[i+pal[i]+1]==P[i-pal[i]-1]) pal[i]++;
		if(i+pal[i]>=C+R) C=i,R=pal[i];
	}
	for(int i=1;i<=t;i++)
		if(pal[i]>ans)
			ans=pal[i],  p1=mp((i-pal[i]+1)/2,(i+pal[i]-1)/2),  p2=mp(-1,-1);

	for(int i=1;i<=t;i++)
	{
		if(P[i]=='#'&&pal[i]==0) continue;
		int zl=(i-pal[i]+1)/2,zr=(i+pal[i]-1)/2;
		int x=pos[zr+1];//现在回文区间是[zl,zr]
		for(int w=20;w>=0;w--)
			if( mi[st[x][w]]>=zl )	x=st[x][w];
		if( mi[x]>=zl ) x = st[x][0];
		int vl=min( l[x],posl[zr+1] ),v=zr-zl+1+vl*2;
		if(v>ans) ans=v,p1=mp( ed[x]-vl+1,ed[x] ),p2=mp(zl,zr+vl);
	}		
}
int main()
{
	scanf("%s",a+1 ); n = strlen( a+1 );
	reverse( a+1,a+1+n );
	work();
	if(p1.fi!=-1) p1.fi=n-p1.fi+1,p1.se=n-p1.se+1,swap(p1.fi,p1.se);
	if(p2.fi!=-1) p2.fi=n-p2.fi+1,p2.se=n-p2.se+1,swap(p2.fi,p2.se);	
	reverse( a+1,a+1+n );
	work();
	
	if(p1.fi>p2.fi) swap(p1,p2);
	printf("%d\n%d %d\n%d %d\n",ans,p1.fi,p1.se,p2.fi,p2.se);
}