原文链接https://www.cnblogs.com/zhouzhendong/p/CF109D.html
题目传送门 - CF109D
题意
给定两个字符串 $a,b$ ,求一组 $i,j$ 使得 $f(a,i,j)=b$ 。如果无解输出 "-1 -1" ,如果多组解,输出 i 尽量大的;如果 i 相同,输出 j 尽量小的。
其中 $f(s,i,j) = s[i+1 \cdots j-1] + r(s[0 \cdots i]) + r(s[j\cdots n-1])$ 。 $n$ 为串长。其中 $s[i\cdots j]$ 表示 串 $s$ 的 $i$ 至 $j$ 个位置构成的子串,+ 运算定义为字符串顺序拼接,$r(s)$ 表示 $s$ 翻转后得到的串。
$|a|,|b| \leq 10^6$
题解
那个最大最小问题不大,我们来看看如何找一组解。
首先,讲 a 串翻转。则问题变成了 $r(a[i+1]\cdots a[j-1])+a[0\cdots i] + a[j\cdots n-1]=b$ 的 $(i,j)$ 。这个问题,只需要枚举 $j$ ,然后判定是否可以行即可。
对于 $r(a[i+1]\cdots a[j-1])$ 与 $b$ 的最大匹配长度,可以二分 + 哈希。
对于以 b 串第 $j-1$ 个位置结尾的子串中与 $a$ 的前缀的匹配长度最大为多少,直接 KMP 。
如果这两个长度大于 $j-1$ ,那么就可行。
那个什么最大最小的,随便弄一弄就好了。
注意我们求出来的翻转后的 a 串的答案,最后还要变换一下。
代码
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=1000005*2;
char a[N],b[N];
int n,Next[N],len[N];
ULL Ha[N],Hb[N],Fac[N],T=233;
ULL Get_Hash(ULL *Ha,int L,int R){
if (L>R)
L=n*2-L+1,R=n*2-R+1;
return Ha[R]-Ha[L-1]*Fac[R-L+1];
}
void KMP(char *a,char *b,int n){
for (int i=2,k=0;i<=n;Next[i]=k+=(a[k+1]==a[i]),i++)
while (k>0&&a[k+1]!=a[i])
k=Next[k];
for (int i=1,k=0;i<=n;len[i]=k+=(a[k+1]==b[i]),i++)
while (k>0&&a[k+1]!=b[i])
k=Next[k];
}
int Get(int j){
int L=1,R=j-1,mid,k1=0;
while (L<=R){
mid=(L+R)>>1;
if (Get_Hash(Hb,1,mid)==Get_Hash(Ha,j-1,j-mid))
L=mid+1,k1=mid;
else
R=mid-1;
}
return k1+len[j-1]<j-1?-1:len[j-1];
}
int main(){
gets(a+1),gets(b+1);
n=strlen(a+1);
if (strlen(b+1)!=n)
return puts("-1 -1"),0;
reverse(a+1,a+n+1);
for (int i=n+1;i<=n*2;i++)
a[i]=a[n*2-i+1],b[i]=b[n*2-i+1];
KMP(a,b,n);
for (int i=Fac[0]=1;i<=n*2;i++){
Fac[i]=Fac[i-1]*T;
Ha[i]=Ha[i-1]*T+a[i];
Hb[i]=Hb[i-1]*T+b[i];
}
int ansi=10000000,ansj=10000000,d=-1;
for (d=n;d>1;d--)
if (a[d]!=b[d])
break;
for (int i=-1,j=d+1;j<=n&&!~i;j++)
if (~(i=Get(j)))
ansi=i,ansj=j;
if (ansj==10000000)
ansi=ansj=-1;
else
ansi=n-ansi,ansj=n-ansj;
printf("%d %d\n",ansj,ansi);
return 0;
}