题目描述:
给你两个字符串 s 1 , s 2 s1,s2 s1,s2,你可以选择一个 s 1 s1 s1的子串对它进行反转,使得 s 1 = s 2 s1=s2 s1=s2,问有几种选法。
解题思路:
预备知识:马拉车算法,不会的话建议百度看看别人的博客。
1、首先如果
s
1
!
=
s
2
s1!=s2
s1!=s2,那么第一个不等的到最后个不等的一点要反转:
1)判断把他们反正是否一样,如果不一样,那么答案就是
0
0
0。
2)如果是一样的,这样一定是一个解,那么我们再往两边扩散看看,如果两边一样那又是一个解,不断重复,知道两边不一样。
2、如果两个字符串完全一样,那么就是求它的回文子串,对于一个长度为
n
n
n的回文子串,它的贡献是
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2,也就是回文半径,这个也比较好理解和上面一样就是以中电向外扩散。那么我们用马拉车算法,计算出每个回文子串的长度就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=4e7+10;
char s1[maxn],s2[maxn];
int p[maxn];
int main(){
int t;
cin>>t;
while(t--)
{
scanf("%s%s",s1,s2);
int len = strlen(s1);
int l = -1;
for(int i=0;i<len;i++)
if(s1[i] != s2[i])
{
l = i;
break;
}
int r = -1;
for(int i=len-1;i>=0;i--)
if(s1[i] != s2[i])
{
r = i;
break;
}
//l代表左边开始第一个不相等的字符的位置
//r代表右边开始第一个不相等的字符的位置
if(l != -1)
{
int f = 1;
for(int i=0;i<(r-l+1);i++)
if(s1[i+l] != s2[r-i])
f=0;
if(f == 0)
printf("0\n");
else
{
int ans = 1;
while(l > 0 && r + 1 < len && s1[l-1] == s1[r+1])
{
l--;
r++;
ans++;
}
printf("%d\n",ans);
}
}
else
{
//马拉车
int n = 1;
s2[0] = '#';
for(int i=0;i<len;i++)
{
s2[n++] = '#';
s2[n++] = s1[i];
}
s2[n++] = '#';
s2[n++] = '*';
p[0] = 1;
int mx = 0;
long long ans = 0;
for(int i=1;i<n-1;i++)
{
p[i] = 1;
if(mx + p[mx] - 1 > i)
p[i] = min(p[mx-(i-mx)],mx+p[mx]-i);
while(s2[i+p[i]] == s2[i-p[i]])
{
p[i]++;
}
if(i+p[i]-1 > mx+p[mx]-1)
mx = i;
ans += p[i]/2;
}
printf("%lld\n",ans);
}
}
return 0;
}