Manacher
参考博客 (懒惰的我):
简单而有通俗的讲解,讲的太好了
证明
对于一些我的理解,我会以代码注释的形式写在代码里, 我不懒
模板
char str[maxn];
char temp[(maxn <<1) + 10];//扩展后的字符串
int Len[(maxn <<1 ) + 10];//扩展后字符串第i个位置回文串从中间到第有边界的长度
//相当于 回文子串长度 / 2 + 1
//在用左移运算符的时候要注意 << 比 + 的优先级要低,要加上括号..
inline int init(int len)
{
temp[0] = '@';//防止越界
for(int i = 1; i <= 2 * len; i += 2){
temp[i] = '#';
temp[i + 1] = str[i / 2];
}
temp[2 * len + 1] = '#';
temp[2 * len + 2] = '$';
temp[2 * len + 3] = 0;
return 2 * len + 1;
}
int manacher(int len,int &pos)
{
int mx = 0, ans = 0, po = 0;
//mx为当前计算的回文字串最右端的字符位置再 + 1
//po为mx对应的回文子串的中点位置,也即得到mx为终点的
//回文子串的起始位置
for(int i = 1; i <= len; i++){
//
if(mx > i) Len[i] = min(mx - i , Len[2 * po - i]);
else Len[i] = 1;
//这里结合了mx == i和mx < i的情况,都是要继续暴力找
//如果第一种情况,由于回文串的性质,第一次循环条件不会成立
while(temp[i - Len[i]] == temp[i + Len[i]]) Len[i]++;
//如果当前回文子串的右边界 > mx那就更新mx
if(i + Len[i] > mx){
po = i;
mx = i + Len[i];
}
if(ans < Len[i]){
ans = Len[i];
pos = i;//找最大长度子串的位置
}
}
return ans - 1;
}
hdu3294
这里说一下扩展后串的位置和原串位置的关系,图上是从0开始,从1开始也是可以的
图片原文链接
这里求一下最长子串的位置就可以了
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <cmath>
#include <cstdlib>
#include <stack>
using namespace std;
const int maxn = 200000 + 5;
const int Size = 20;
char str[maxn];
char temp[(maxn <<1) + 10];//扩展后的字符串
int Len[(maxn <<1 ) + 10];//扩展后字符串第i个位置回文串从中间到第有边界的长度
//相当于 回文子串长度 / 2 + 1
//在用左移运算符的时候要注意 << 比 + 的优先级要低,要加上括号..
inline int init(int len)
{
temp[0] = '@';//防止越界
for(int i = 1; i <= 2 * len; i += 2){
temp[i] = '#';
temp[i + 1] = str[i / 2];
}
temp[2 * len + 1] = '#';
temp[2 * len + 2] = '$';
temp[2 * len + 3] = 0;
return 2 * len + 1;
}
int manacher(int len,int &pos)
{
int mx = 0, ans = 0, po = 0;
//mx为当前计算的回文字串最右端的字符位置再 + 1
//po为mx对应的回文子串的中点位置,也即得到mx为终点的
//回文子串的起始位置
for(int i = 1; i <= len; i++){
//
if(mx > i) Len[i] = min(mx - i , Len[2 * po - i]);
else Len[i] = 1;
//这里结合了mx == i和mx < i的情况,都是要继续暴力找
//如果第一种情况,由于回文串的性质,第一次循环条件不会成立
while(temp[i - Len[i]] == temp[i + Len[i]]) Len[i]++;
//如果当前回文子串的右边界 > mx那就更新mx
if(i + Len[i] > mx){
po = i;
mx = i + Len[i];
}
if(ans < Len[i]){
ans = Len[i];
pos = i;//找最大长度子串的位置
}
}
return ans - 1;
}
int main()
{
char q[2];
while(scanf("%s%s",q,str) == 2){
char c = q[0];
int len = strlen(str);
int nlen = init(len);
int pos = -1;
int ans = manacher(nlen,pos);
if(ans < 2){
printf("No solution!\n");
continue;
}
//最长回文子串在原字符串中的左右边界
//因为每一个回文子串结尾的左右两端都是'#'
//那么我们左右都找ans-1个字符保证这时候
//这个字符串左右两端都是原串字符这样直接除2就可以得到最长子串的左右端点
int l = (pos - ans + 1)/ 2 - 1;
int r = (pos + ans - 1)/ 2 - 1;
printf("%d %d\n",l,r);
for(int i = l; i <= r; i++){
int x = c -'a';
if(str[i] - x >= 'a') printf("%c",str[i] - x);
else printf("%c",str[i] + 26 -x);
}
printf("\n");
}
return 0;
}
hdu3068
裸题
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <cmath>
#include <cstdlib>
#include <stack>
using namespace std;
const int maxn = 110000 + 5;
const int Size = 20;
char str[maxn];
char temp[maxn<<1 + 10];
int Len[maxn<<1 + 10];
inline int init(int len)
{
temp[0] = '@';
for(int i = 1; i <= 2 * len; i += 2){
temp[i] = '#';
temp[i + 1] = str[i / 2];
}
temp[2 * len + 1] = '#';
temp[2 * len + 2] = '$';
temp[2 * len + 3] = 0;
return 2 * len + 1;
}
int manacher(int len)
{
int mx = 0, ans = 0, po = 0;
//mx为当前计算的回文字串最右端的字符位置再 + 1
//po为mx对应的回文子串的中点位置,也即得到mx为终点的
//回文子串的起始位置
for(int i = 1; i <= len; i++){
if(mx > i) Len[i] = min(mx - i , Len[2 * po - i]);
else Len[i] = 1;
while(temp[i - Len[i]] == temp[i + Len[i]]) Len[i]++;
if(i + Len[i] > mx){
po = i;
mx = i + Len[i];
}
ans = max(ans,Len[i]);
}
return ans - 1;
}
int main()
{
while(scanf("%s",str) == 1){
int len = strlen(str);
int nlen = init(len);
int ans = manacher(nlen);
printf("%d\n",ans);
}
return 0;
}