题目大意:有两个字符串s,t,问能否将s的一段[l,r]子串翻转使得它等于t? 如果可以,问有多少段这样的子串使得翻转一次后s = t,注:只能翻转一次。
解法:简单题,分情况讨论:如果s 原本就等于 t,这个时候s中所有回文串的半径就是答案。若s 不等于 t,则找出不相等的那一段区间的最左边和最右边,check一下中间这段能否通过翻转使得相等,如果不能答案 = 0,否则可以像两边延申判断l,r 是否可以翻转使得s = t,答案为可以延申的长度。
这里留意一下马拉车统计答案的方式,只要明白算出来的东西是什么,可以打个表看一下,找规律然后统计。
#include<bits/stdc++.h>
using namespace std;
int ca;
const int maxn = 2e6 + 10;
char s[maxn],t[maxn];
char tmp[2 * maxn];
int len[2 * maxn],tot;
bool strcmp(char s[],char t[],int l,int r) {
for(int i = l; i <= r; i++)
if(s[i] != t[i]) return false;
return true;
}
bool check(char s[],char t[],int l,int r) {
for(int i = l; i <= r; i++) {
if(s[i] != t[r - i + l]) return false;
}
return true;
}
void init(int p) {
tot = 0;
tmp[tot++] = '$';
for(int i = 0; i < p; i++) {
tmp[tot++] = '#';
tmp[tot++] = s[i];
}
tmp[tot++] = '#';
}
long long manacher() {
len[0] = 0;
int mx = -1;
int id = -1;
long long res = 0;
for(int i=0;i<tot;i++){
if(i < mx) len[i] = min(mx - i, len[2 * id - i]);
else len[i] = 1;
while(i - len[i] >= 0 && i + len[i] < tot && tmp[i - len[i]]== tmp[i + len[i]]) len[i]++;
if(len[i] + i > mx){
mx = len[i] + i;
id = i;
}
// printf("%c %d\n",tmp[i],len[i]);
res += len[i] / 2;
}
return res;
}
int main() {
scanf("%d",&ca);
while(ca--) {
tot = 0;
scanf("%s",s);
scanf("%s",t);
int p = strlen(s);
if(!strcmp(s,t,0,p - 1)) {
int l = -1,r = -1;
for(int i = 0; i < p; i++) {
if(s[i] != t[i]) {
l = i;
break;
}
}
for(int i = p - 1; i >= 0; i--) {
if(s[i] != t[i]) {
r = i;
break;
}
}
if(!check(s,t,l,r)) {
printf("%d\n",0);
}
else {
int res = 0;
while(l >= 0 && r < p) {
if(s[l] == t[r] && t[l] == s[r]) {
res++;
l--;r++;
}
else break;
}
printf("%d\n",res);
}
}
else {
init(p);
printf("%lld\n",manacher());
}
}
return 0;
}