链接 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4110
题意:给你两个字符串s和t,问你有多少种方法,在仅翻转一次s的子串后使得两个串相同。
题解:分两种情况。
1.两个串不同
我们先找到两个串不同点的边界值l,r,也就是最左边的那个不同点和最右边的那个不同的点。
显然可得如果翻转后两个串相同的话,那最小的翻转区间就是并且必须是[l,r].
反证明如下:
如果我们翻转区间的左端点大于l,或者右端点小于r,那 l / r 肯定没有改变,也就是说翻转完以后两个串不可能相同。
而如果我们以 l 为左端点,以x(x>r)为右端点,那我们可知s[x]=t[x]。假如翻转后两个串相等,那就会有s[l]=t[x],s[x]=t[l] ,加上s[x]=t[x] ,得出s[l]=t[l],与我们s[l]!=t[l]的条件矛盾。r同理。
然后我们从l,r向内递推判断翻转后是否相等,也就是:
while(L<=R&&L>=0&&R<len1){
if(s[L]!=t[R]||s[R]!=t[L]){
f=0;
break;
}
L++,R--;
}
如果我们得到[l,r]翻转之后能使得两个串相同,那我们接下来就可以尝试往外拓展,
ans=0;
while(s[l]==t[r]&&s[r]==t[l]&&l>=0&&r<len1){
ans++;
l--,r++;
}
这样就能得到答案。
2.两个串完全相同
该情况涉及到一个算法:Manacher
不会的可以先去看看这篇博客:https://blog.csdn.net/happyrocking/article/details/82622881
Manacher算法实现的就是求出以字符串每个点(包括两个点之间的边界)为中心的最大回文串长度。时间复杂度是O(n)。
如果两个串相同,那我们只要翻转s串中任意一个回文串,这两个串就会保持原状,而使用Manacher算法能让我们在线性时间内求出每个点为中心的最大回文串长度。
对于任意一个中心点,我们翻转半径小于该点最大回文串半径的字串,就相当于翻转了一个回文串,原串保持不变,因此在该点我们可以进行的操作数x即为该点最大回文串半径的值。
for(int i=1;i<len2;i++){//len2为Manacher处理过后的串长。
ans+=p[i]/2;//p[i]为以该点为中心的最大回文串长+1,因此我们除以2就能得到半径。
}
完整代码:
#include<stdio.h>
#include<string.h>
const int N=4e6;
char str[N<<1];
int p[N];
int len2,len1;
char s[N],t[N];
int min(int a,int b){
return a<b?a:b;
}
int max(int a,int b){
return a>b?a:b;
}
void Manacher(){
for(int i=0;i<len1*2+4;i++)p[i]=0;
str[0]='$',str[1]='#';
for(int i=0;i<len1;i++){
str[i*2+2]=s[i];
str[i*2+3]='#';
}
int mx=0,id=0;
len2=len1*2+2;
str[len2]='#';
for(int i=1;i<len2;i++){
p[i]=i<mx?min(p[2*id-i],mx-i):1;
while(str[i+p[i]]==str[i-p[i]])++p[i];
if(i+p[i]>mx)mx=i+p[i],id=i;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%s%s",s,t);
len1=strlen(s);
int l=len1,r=-1,flag=1;
for(int i=0;i<len1;i++){
if(s[i]!=t[i]){
l=min(l,i);
r=max(r,i);
flag=0;
}
}
if(flag){
Manacher();
long long ans=0;
for(int i=1;i<len2;i++){
ans+=p[i]/2;
}
printf("%lld\n",ans);
}
else{
int L=l,R=r;
int f=1;
while(L<=R&&L>=0&&R<len1){
if(s[L]!=t[R]||s[R]!=t[L]){
f=0;break;
}
L++,R--;
}
int ans=0;
if(f==0){
ans=0;
}
else{
ans=0;
while(s[l]==t[r]&&s[r]==t[l]&&l>=0&&r<len1){
ans++;
l--,r++;
}
}
printf("%d\n",ans);
}
}
return 0;
}