一. 题目
二. 思路
若该串前缀和后缀有一部分成回文, 那么翻转时一定不会翻转已经成回文的部分
假设t位置是不等于n-t+1位置, 那么翻转的子串一定是以t开头或者n-t+1结尾, 否则翻转后也一定不是回文串
所以可以暴力枚举i:t~n-t+1, check(t, i)||check(i, n-t+1)
如何判断翻转后的值, 则利用字符串哈希, 求出前高后低head, 和前低后高tail, 注意这里求tail时是将其反过来和求head时一块求, 这样可以共用一个get_hash函数, 详见make_hash函数
若对于正串的(l, r)这段, 对于反串则是(n-r+1, n-l+1)
三. 代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
using LL=long long;
const int N=5e5+10, S=131, MOD=1e9+7;
int T;
int n;
char str[N];
class StrHash
{
public:
LL head[N], tail[N], sys[N];
public:
StrHash()
{
initIO();
}
void initIO()
{//卡cin和cout
// ios::sync_with_stdio(false);
// cin.tie(nullptr), cout.tie(nullptr);
}
void makeHash()
{
sys[0]=1, head[0]=tail[0]=0;
for(int i=1, j=n;i<=n;++i, --j)
{//head abc, tail cba
sys[i]=getMod(sys[i-1]*S);//head[i]表示以1~i这段前高后低的哈希值
head[i]=getMod(head[i-1]*S+str[i]);//高位~低位
tail[i]=getMod(tail[i-1]*S+str[j]);//tail是把尾当作头,把头当做尾,低位~高位
//tail[i]表示,n ~ n-i+1这段,前高后低的哈希值,这样可以共用同一个getHash函数
}
}
LL getHash(LL has[], int l, int r)
{
// return getMod(hash[r]-hash[l-1]*sys[r-l+1]);
return getMod(has[r]-has[l-1]*sys[r-l+1]);
}
LL getMod(LL x)
{
return (x%MOD+MOD)%MOD;
}
bool reverIfH(int l, int r)//翻转l~r的区间后,是否是回文串
{
LL h=head[n], t=tail[n];//最初全串前后哈希值
//减去正串*进制差值, +上倒串对应位置*进制差值:......rxxxx n-r就求出了xxx需要乘的进制
LL nh=getMod(h-getHash(head, l, r)*sys[n-r]+getHash(tail, n-r+1, n-l+1)*sys[n-r]);
LL nt=getMod(t-getHash(tail, n-r+1, n-l+1)*sys[l-1]+getHash(head, l, r)*sys[l-1]);
//减去倒着的加上正着的, n-(n-l+1)->求出进制差:l-1
return nh==nt;
}
}sh;
int main()
{
cin>>T;
while(T--)
{
scanf("%s", str+1);
n=strlen(str+1);
sh.makeHash();
if(sh.head[n]==sh.tail[n])
{
puts("Yes");
continue;
}
int t;
for(int i=1;i<=n;++i)
if(str[i]!=str[n-i+1])//找到第一次不相等的地方
{
t=i;
break;
}
bool ifH=false;
for(int i=t;i<=n-t+1;++i)//暴力翻转所有情况
if(sh.reverIfH(t, i)||sh.reverIfH(i, n-t+1))
{
ifH=true;
break;
}
if(ifH) puts("Yes");
else puts("No");
}
}