框架代码,对应蓝桥云课 最长回文子串 代码见下
cpp
#include <iostream>
#include <cstring>
using namespace std;
#define maxn 1000010
#define split '$'
int p[maxn];
char strTmp[maxn];
void ManacherPre(char* str) {
strcpy(strTmp, str);
int i = 0;
for (; strTmp[i]; ++i) {
str[2 * i] = split;
str[2 * i + 1] = strTmp[i];
}
str[2 * i] = split;
str[2 * i + 1] = '\n';
}
bool ManacherMatch(char a, char b) {
return a == b;
}
int Manacher(char * str) {
ManacherPre(str);
// ct : 当前已知的最优回文区域的中心位置
int ct = 0;
// r : 当前已知的最右回文区域的右边界(ct + p[ct])
int r = 0;
int maxLen = 1;
// p[] : 记录每个位置的回文半径
p[0] = 1;
for (int i = 1; str[i]; ++i) {
// 1 计算p[i]的初始值
if (i < r) {
p[i] = min(p[2 * ct - i], r - i);
}
else {
p[i] = 0;
}
// 2 扩张p[i],让p[i]达到最大值
while (i - p[i] >= 0 && ManacherMatch(str[i - p[i]], str[i + p[i]])) {
++p[i];
}
// 3 跟新ct 和 r
if (i + p[i] > r) {
ct = i;
r = i + p[i];
}
// 4 更新最长回文
if (2 * p[i] - 1 > maxLen) {
maxLen = 2 * p[i] - 1;
}
}
return maxLen;
}
char str[maxn];
int main()
{
cin >> str;
int ans = Manacher(str);
cout << ans / 2 << endl;
// 请在此输入您的代码
return 0;
}
代码练习 1 对应蓝桥云课 判定回文串 代码见下
cpp
#include <iostream>
#include <cstring>
using namespace std;
#define maxn 1000010
#define split '$'
int p[maxn];
char strTmp[maxn];
void ManacherPre(char* str) {
strcpy(strTmp, str);
int i = 0;
for (; strTmp[i]; ++i) {
str[2 * i] = split;
str[2 * i + 1] = strTmp[i];
}
str[2 * i] = split;
str[2 * i + 1] = '\n';
}
bool ManacherMatch(char a, char b) {
return a == b;
}
int Manacher(char* str) {
ManacherPre(str);
// ct : 当前已知的最优回文区域的中心位置
int ct = 0;
// r : 当前已知的最右回文区域的右边界(ct + p[ct])
int r = 0;
int maxLen = 1;
// p[] : 记录每个位置的回文半径
p[0] = 1;
for (int i = 1; str[i]; ++i) {
// 1 计算p[i]的初始值
if (i < r) {
p[i] = min(p[2 * ct - i], r - i);
}
else {
p[i] = 0;
}
// 2 扩张p[i],让p[i]达到最大值
while (i - p[i] >= 0 && ManacherMatch(str[i - p[i]], str[i + p[i]])) {
++p[i];
}
// 3 跟新ct 和 r
if (i + p[i] > r) {
ct = i;
r = i + p[i];
}
// 4 更新最长回文
if (2 * p[i] - 1 > maxLen) {
maxLen = 2 * p[i] - 1;
}
}
return maxLen;
}
char str[maxn];
int main()
{
int n, m;
cin >> n >> m;
cin >> str;
Manacher(str);
while (m--) {
int l, r;
cin >> l >> r;
l = l * 2 - 1;
r = r * 2 - 1;
int mid = (l + r) / 2;
if (mid + p[mid] - 1 >= r) {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
}
return 0;
}
代码练习2 小蓝的01串, 对应蓝桥云课 代码见下
cpp
/*
1 首先,满足要求的子串一定是偶数长度的
反证法证明,假设是技术,见下:
原串 a b c
取反 1-a 1-b 1-c
反转 1-c 1-b 1-a
这时候,就会发现,1-b==b显然不合理,所以子串长度为奇数的话,一定是不合法
2 利用同样的方法,观察偶数的情况
原串 a b c d
取反 1-a 1-b 1-c 1-d
反转 1-d 1-c 1-b 1-a
得出结论:a+d == 1 b+c == 1
3 这样一来,只需要把原先马拉车的模版,两边字符相等的逻辑,改成字符相加等于1即可
4 最后,统计偶数串的个数,做个简单的技术操作即可
*/
#include <iostream>
#include <cstring>
using namespace std;
#define maxn 1000010
#define split '$'
int p[maxn];
char strTmp[maxn];
void ManacherPre(char* str) {
strcpy(strTmp, str);
int i = 0;
for (; strTmp[i]; ++i) {
str[2 * i] = split;
str[2 * i + 1] = strTmp[i];
}
str[2 * i] = split;
str[2 * i + 1] = '\n';
}
bool ManacherMatch(char a, char b) {
if(a == b){
return a == split;
}
return (a - '0') + (b - '0') == 1;
}
int Manacher(char * str) {
ManacherPre(str);
// ct : 当前已知的最优回文区域的中心位置
int ct = 0;
// r : 当前已知的最右回文区域的右边界(ct + p[ct])
int r = 0;
int maxLen = 1;
// p[] : 记录每个位置的回文半径
p[0] = 1;
for (int i = 1; str[i]; ++i) {
// 1 计算p[i]的初始值
if (i < r) {
p[i] = min(p[2 * ct - i], r - i);
}
else {
p[i] = 0;
}
// 2 扩张p[i],让p[i]达到最大值
while (i - p[i] >= 0 && ManacherMatch(str[i - p[i]], str[i + p[i]])) {
++p[i];
}
// 3 跟新ct 和 r
if (i + p[i] > r) {
ct = i;
r = i + p[i];
}
// 4 更新最长回文
if (2 * p[i] - 1 > maxLen) {
maxLen = 2 * p[i] - 1;
}
}
return maxLen;
}
char str[maxn];
int main()
{
int n;
cin >> n;
cin >> str;
Manacher(str);
long long ans = 0;
for(int i=0; str[i]; i += 2){
ans += p[i]/2;
}
cout << ans << endl;
return 0;
}
代码练习3 反异或01串 对应蓝桥云课 代码见下
cpp
/*
1 由于反异或操作只能进行一次,所以我们就恶意挑选其中一段来进行反异或操作,而剩下的段,统计1的个数就可以的
于是问题就变成"如何找到需要反异或的段"
2 考虑这种情况,先考虑奇数的情况
a b c d e
^ ^ ^ ^ ^
e d c b a
2 满足可以反异或的前提,中间这个位置必须是0,因为自己异或自己必然是0
然后由于异或满足交换律,也就是b*d = d*b,所以就变成了求回文串的情况了
同样偶数也是一样的,只不过偶数不需要判定中间为0的情况
3 利用manacher计算出每个位置作为回文串中心的最大半径p[i]
并且求区间中[i, i+p[i]-1]中1的个数,代表反异或操作之前需要的1
再求区间[i-p[i]+1, i+p[i]-1]以外的1的个数代表不进行反异或操作段中需要的1
把两者累加即可
*/
#include <iostream>
#include <cstring>
using namespace std;
#define maxn 2000010
#define split '$'
int p[maxn];
char strTmp[maxn];
void ManacherPre(char* str) {
strcpy(strTmp, str);
int i = 0;
for (; strTmp[i]; ++i) {
str[2 * i] = split;
str[2 * i + 1] = strTmp[i];
}
str[2 * i] = split;
str[2 * i + 1] = '\n';
}
bool ManacherMatch(char a, char b) {
return a == b;
}
int Manacher(char* str) {
ManacherPre(str);
// ct : 当前已知的最优回文区域的中心位置
int ct = 0;
// r : 当前已知的最右回文区域的右边界(ct + p[ct])
int r = 0;
int maxLen = 1;
// p[] : 记录每个位置的回文半径
p[0] = 1;
for (int i = 1; str[i]; ++i) {
// 1 计算p[i]的初始值
if (i < r) {
p[i] = min(p[2 * ct - i], r - i);
}
else {
p[i] = 0;
}
// 2 扩张p[i],让p[i]达到最大值
while (i - p[i] >= 0 && ManacherMatch(str[i - p[i]], str[i + p[i]])) {
++p[i];
}
// 3 跟新ct 和 r
if (i + p[i] > r) {
ct = i;
r = i + p[i];
}
// 4 更新最长回文
if (2 * p[i] - 1 > maxLen) {
maxLen = 2 * p[i] - 1;
}
}
return maxLen;
}
char str[maxn];
int sum[maxn];
int getsum(int l, int r) {
int pre = 0;
if (l) {
pre = sum[l - 1];
}
return sum[r] - pre;
}
int main()
{
cin >> str;
Manacher(str);
int n = strlen(str);
for (int i = 1; i < n; ++i) {
sum[i] = sum[i - 1] + (str[i] == '1');
}
int ret = sum[n - 1];
for (int i = 0; i < n; ++i) {
if (i & 1) {
//奇数位置,以数字为中心
if (str[i] == '1') {
//中间数不能为1
continue;
}
}
else {
//偶数位置,以$为中心
}
//统计[i, i+p[i]-1]中1的个数
int ans = getsum(i, i + p[i] - 1) +
//在统计[i-p[i]+1, i+p[i]-1]以外1的个数
getsum(0, n - 1) - getsum(i - p[i] + 1, i + p[i] - 1);
ret = min(ans, ret);
}
cout << ret << endl;
return 0;
}