题意:
分析:
- 暴力:
对于PAM的DP题目,可能需要将题目转化一下,将原串变为 (s_1t_1s_2t_2dots s_nt_n) ,这样对于一个需要翻转的区间 ([i,j]) 满足 (s_i=t_j,s_{i+1}=t_{j-1}) 即区间 ([i,j]) 在新构造的串上是一个偶回文子串,题目转化为对新串求最小偶回文分割方案,建出SAM暴力DP,转移式 (f[i]=min {f[i],f[j]+1且s[j:i]为回文串}),因为 (PAM) 的结构,所以 (s[j:i]) 是 (s[1:i]) 的一个回文后缀所以跳 (fail) 就能找到所有的 (j),复杂度理论上界 (O(n^2))
- 正解
由于PAM的树高可以达到(O(n)),所以暴力跳 (fail) 是会被卡的,所以我们需要利用 PAM 的另一个结论,由于串 S 的所有回文后缀排序后可以分为 (O(log n)) 个等差数列,所以我们可以对于每一个等差数列进行转移,复杂度(O(nlog n))
tip:
要特判长度为 2 的回文串划分是不需要代价
代码:
#include<bits/stdc++.h>
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
const int maxn = 1e6+5;
int m,n;
int f[maxn],whe[maxn],lpos[maxn];
char ch[maxn],a[maxn],b[maxn];
namespace PAM
{
int tot,lst;
int len[maxn],trans[maxn][26],fail[maxn],slink[maxn],dif[maxn];
inl void init()
{
len[0]=0;len[1]=-1;
fail[0]=1;fail[1]=-1;
tot=1;lst=0;
}
inl int get_fail(int x,int l)
{
while(ch[l-len[x]-1]!=ch[l]) x=fail[x];
return x;
}
inl void insert(int c,int l)
{
int p=get_fail(lst,l);
if(!trans[p][c])
{
int cur=++tot;
len[cur]=len[p]+2;
int tmp=get_fail(fail[p],l);
fail[cur]=trans[tmp][c];
trans[p][c]=cur;
dif[cur]=len[cur]-len[fail[cur]];
if(dif[cur]==dif[fail[cur]]) slink[cur]=slink[fail[cur]];
else slink[cur]=fail[cur];
}
lst=trans[p][c];
}
}
using namespace PAM;
void work()
{
scanf("%s",a+1);scanf("%s",b+1);n=strlen(a+1);
for(reg int i=1;i<=n;i++) ch[++m]=a[i],ch[++m]=b[i];
init();for(reg int i=1;i<=m;i++) f[i]=1e9;
ch[0]=ch[m+1]=100;whe[0]=1;
for(reg int i=1;i<=m;i++)
{
insert(ch[i]-'a',i);
for(reg int x=lst;x;x=slink[x])
{
whe[x]=i-len[slink[x]]-dif[x];
if(dif[fail[x]]==dif[x]&&f[whe[x]]>f[whe[fail[x]]]) whe[x]=whe[fail[x]];
if(i%2==0&&f[i]>f[whe[x]]+1) f[i]=f[whe[x]]+1,lpos[i]=whe[x];
if(i%2==0&&ch[i]==ch[i-1]&&f[i]>f[i-2]) f[i]=f[i-2],lpos[i]=i-2;
}
}
if(f[m]>=1e9)
{
puts("-1");
return ;
}
printf("%d
",f[m]);
for(reg int x=m;x;x=lpos[x]) if(x-lpos[x]!=2) printf("%d %d
",lpos[x]/2+1,x/2);
}
}
int main()
{
zzc::work();
return 0;
}