目录
原理
一句话:左边算过,右边可以直接(范围允许内)用。
r记录当前最右的回文(l(左)与之对应),这样我们后来在r中偏右进行判断时,因为l r之间是回文,所以可以参照中偏左对应的位置,少判断许多次。
使用示范,本板子是加#(奇偶长度一起算)的:
d[i]表示以位置i为中心的最长回文串的半径长度
d数组的值-1即是本位置最长回文长度,原因看最下面注释。
void solve()
{
string str, aim;
cin >> str;
aim += "#";
for (int i = 0; i < str.size(); i++)
{
aim += str[i];
aim += "#";
}
vector<int>d(aim.size());
// abcba
auto manacher = [&](string s) {
int l = 0, r = -1;
for (int i = 0; i < s.size(); i++)
{
int k = i > r ? 1 : min(d[l + r - i], r - i + 1);//左边对称相同,但不越界
//k是半径,加了等于下一位
//朴素算法
while (i - k >= 0 && i + k <= s.size() && s[i - k] == s[i + k])
k++;
d[i] = k;
if (i + d[i] - 1 > r)
{
r = i + d[i] - 1;
l = i - d[i] + 1;
}
}
};
manacher(aim);
int ans = 0;
for (auto x : d)
{
ans = max(ans, x);
}
//d[i]表示i(不包括i)左右的对称的长度
// # a # b # a #
// - - - -
// # a # b # b # a #
// - - - - -
cout << ans - 1;
}
单独lamda:
// abcba
auto manacher = [&](string s) {
int l = 0, r = -1;
for (int i = 0; i < s.size(); i++)
{
int k = i > r ? 1 : min(d[l + r - i], r - i + 1);//左边对称相同,但不越界
//k是半径,加了等于下一位
//朴素算法
while (i - k >= 0 && i + k <= s.size() && s[i - k] == s[i + k])
k++;
d[i] = k;
if (i + d[i] - 1 > r)
{
r = i + d[i] - 1;
l = i - d[i] + 1;
}
}
};
————
OI Wiki摘录的只算单数和双数的:
vector<int> d1(n);
for (int i = 0, l = 0, r = -1; i < n; i++) {
int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);
while (0 <= i - k && i + k < n && s[i - k] == s[i + k]) {
k++;
}
d1[i] = k--;
if (i + k > r) {
l = i - k;
r = i + k;
}
}
vector<int> d2(n);
for (int i = 0, l = 0, r = -1; i < n; i++) {
int k = (i > r) ? 0 : min(d2[l + r - i + 1], r - i + 1);
while (0 <= i - k - 1 && i + k < n && s[i - k - 1] == s[i + k]) {
k++;
}
d2[i] = k--;
if (i + k > r) {
l = i - k - 1;
r = i + k;
}
}
练习:
其实是想随便做一道简单题备赛蓝桥,也没看标签。
蓝桥杯是不允许带纸质资料的,所以只能手搓板子。我忘了这个板子了,复习了下干什么的,搓完还是出错了(即右边用了左边的,但是超过当前右边界了。)
用了样例 101010 才发现。
参考代码:
向下取整同时处理了奇数回文和偶数回文,自己举例观察一下。
#include <bits/stdc++.h>
using namespace std;
int main()
{
//00111011
//10 ^ 10 == 11
//11000 操作后为 11011
//
//找最长回文串,但是这个长度应为1的数目
//a#c#c#c#a
// ---
// ---
//
string str;
cin >> str;
string aim;
aim += ' ';
aim += str[0];
for (int i = 1; i < str.size(); i++)
{
aim += '#';
aim += str[i];
}
vector<int>pre(aim.size());
int cnt1 = 0;
for (int i = 1; i < aim.size(); i++)
{
if (aim[i] == '1')
{
pre[i]++;
cnt1++;
}
pre[i] += pre[i - 1];
}
int maxn = 0;
int n = aim.size();
vector<int>manacher(n);
int mr = 1, mi = 1;
for (int i = 1; i < n; i++)
{
int k = 1;
if (i < mr)
{
//k = manacher[mi-(mr-mi)+mr-i];
k = min(mr-i,manacher[mi-(i-mi)]);
}
while (0 < i - k && i + k < n)
{
if (aim[i - k] == aim[i + k])
{
k++;
}
else
break;
}
k--;
manacher[i] = k;
if (i + k > mr)
{
mr = i + k;
mi = i;
}
maxn = max(maxn, pre[i + k] - pre[i - k - 1]);
}
cout << cnt1 - maxn / 2 << endl;
return 0;
}