8题签到之一 链接:
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=6012
题目大意:你可以对第一串的任意区间进行翻转,使得翻转该区间之后第一串等于第二串,现求这样的区间有多少个。
思路:啊好明显的马拉车模板题,如果两串存在不同,判断是否能通过翻转变成相同;而如果两串相同,求所有回文子串的个数即可。
代码里的马拉车是好久以前扒某博客的,忘了从哪扒的。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2000010;
char st[maxn];//原字符串
char ss[maxn];//原字符串
char s[maxn];
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
ll ans;
int INIT(char *st,int len)
{
int i;
tmp[0]='@';//字符串开头增加一个特殊字符,防止越界
for(i=1;i<=2*len;i+=2)
{
tmp[i]='#';
tmp[i+1]=st[i/2];
}
tmp[2*len+1]='#';
tmp[2*len+2]='$';//字符串结尾加一个字符,防止越界
tmp[2*len+3]=0;
return 2*len+1;//返回转换字符串的长度
}
void MANACHERR(int len)
{
int mx=0,po=0;//mx即为当前计算回文串最右边字符的最大值
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(tmp[i-Len[i]]==tmp[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
}
if(tmp[i]=='#')
ans+=(Len[i]-1)/2;
else if(tmp[i]>='a'&&tmp[i]<='z')
ans+=Len[i]/2;
}
}
int main() {
int n,m,q,i,j,k,num,sum,l,r;
scanf("%d",&q);
while(q--)
{
scanf("%s%s",st,ss);
ans=r=l=sum=0;
int len=strlen(st);
int sym=0;
for(i=0;i<len;i++)
{
if(st[i]!=ss[i]) //找不相等的最大区间
{
if(sym==0)
l=r=i,sym=1;
else
r=i;
}
}
if(sym==1)
{
int tot=r;
for(i=l;i<=r;i++)
{
if(st[i]!=ss[tot]) //判断是否能通过翻转变成相等
{
sym=2;
printf("0\n");
break;
}
tot--;
}
}
if(sym==1)
{
for(i=l-1;i>=0&&r+l-i<len;i--) // 翻转区间两边的回文串长度 假设翻转区间为 abc ,显然 qqabcqq也是可行的区间
{
if(st[i]==st[r+l-i])
ans++;
else
break;
}
printf("%lld\n",ans+1); // +1为翻转区间
continue;
}
else if(sym==0)
{
int qq=INIT(st,len); //统计所有回文子串
MANACHERR(qq);
printf("%lld\n",ans);
}
}
return 0;
}