Strings in the Pocket(2019浙江省省赛)(马拉车-Manacher)
Time limit:1000 ms
Memory limit:65536 kB
judge:
ZOJ
vjudge
Description
BaoBao has just found two strings and in his left pocket, where indicates the -th character in string , and indicates the -th character in string .
As BaoBao is bored, he decides to select a substring of and reverse it. Formally speaking, he can select two integers and such that and change the string to .
In how many ways can BaoBao change to using the above operation exactly once? Let be an operation which reverses the substring , and be an operation which reverses the substring . These two operations are considered different, if or .
Input
There are multiple test cases. The first line of the input contains an integer , indicating the number of test cases. For each test case:
The first line contains a string (), while the second line contains another string (). Both strings are composed of lower-cased English letters.
It’s guaranteed that the sum of of all test cases will not exceed .
Output
For each test case output one line containing one integer, indicating the answer.
Sample Input
2
abcbcdcbd
abcdcbcbd
abc
abc
Sample Output
3
3
Hint
For the first sample test case, BaoBao can do one of the following three operations: (2, 8), (3, 7) or (4, 6).
For the second sample test case, BaoBao can do one of the following three operations: (1, 1), (2, 2) or (3, 3).
题意
给你两个字符串 和 ,你可以选择 的一个区间,然后区间内的元素左右翻转。此操作只能执行一次。问你有多少种操作可以使 变为 。
题解
假设 可以变为 (废话)
那么还要分类讨论一下
- 当 不等于 时,此时 和 一定有一段区间可以通过反转来变为 的对应区间。那么这一段区间就是一种答案。此外一次区间为基准向外扩展,因为如果此区间外左右两边的元素相同的话换与不换都不影响结果,所以包含他们就又是一种答案,以此类推,外面有几层相同的,答案就加几。
- 当 等于 时,我们可以找到一个回文子串,然后反转这个区间,效果就相当于没反转,那么 和 还是相同的。所以问题就转化为了求 的回文子串的数目。
由此想到了马拉车算法,因为p数组的意义就是以当前位置为中心的回文串的最大宽度。那么宽度就是回文串的半径,也就是可以组成的回文串的个数。但是由于马拉车算法往字符里塞了一些特殊字符,所以对p[i]/2
求和即可。
代码
#include <iostream>
#include <cstring>
#define maxn 4000005
using namespace std;
int p[maxn];
int T;
char str[maxn / 2], ttr[maxn / 2];
char ss[maxn];
long long manacher() {
int len = 0;
ss[len++] = '$';
ss[len++] = '#';
int n = strlen(str);
for (int i = 0; i < n; i++) {
ss[len++] = str[i];
ss[len++] = '#';
}
int mx = 0, id = 0;
for (int i = 1; i < len; i++) {
if (mx > i) p[i] = (p[2 * id - i] < (mx - i) ? p[2 * id - i] : (mx - i));
else p[i] = 1;
while (i - p[i] >= 0 && i + p[i] < len && ss[i - p[i]] == ss[i + p[i]]) p[i]++;
if (i + p[i] > mx) {
mx = i + p[i];
id = i;
}
}
long long ans = 0;
for (int i = 2; i < len - 1; ++i) {
if (ss[i] == '#') ans += p[i] / 2;
else ans += (p[i] + 1) / 2;
}
return ans;
}
int getno() {
int ans = 1, len = strlen(str);
int l = 0, r = len - 1;
while (l < r && str[l] == ttr[l]) ++l;
while (r > l && str[r] == ttr[r]) --r;
for (int i = l, j = r; i <= r; ++i, --j) if (str[i] != ttr[j]) return 0;
for (int i = l - 1, j = r + 1, le = len; i >= 0 && j < le && str[i] == ttr[j]; --i, ++j) ++ans;
return ans;
}
int main() {
while (cin >> T) {
for (int i = 0; i < T; ++i) {
scanf("%s%s", str, ttr);
if (strcmp(str, ttr) == 0) cout << manacher() << "\n";
else cout << getno() << "\n";
}
}
return 0;
}