Manacher算法解决的问题?
马拉车算法 Manacher‘s Algorithm 是用来查找一个字符串的最长回文子串的线性方法。
Manacher算法的几个概念:
1.pArr[] 每个位置上的回文半径。半径长度是包括原点的嘛。
2.R 当前最远右边扩展到的位置。(回文右边界)
3.C 得到R 的回文中心。
Manacher算法需要处理的情况:
1. 当 i 不在回文右边界里面:暴力扩展。
2. 当i 在回文右边界里面:
a. i' 回文在L,R内 , i 的回文半径 等于 i'的回文半径。
b. i'回文在L,R 外 , i 的回文半径 等于 R - i。
c. i'回文左边界与L压线 ,i的回文半径要从R向右扩。
注意:L表示回文左边界,i' 表示 与 i 对称轴为 C 对称的点。
可以看到,i 的 回文半径与i' 有关。‘
例题:
最长回文
题意:一个字符串找它的最长回文子串。
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#include<climits>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
//const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 102
//#define Mod 10000
using namespace std;
const int maxn = 110000 + 5;
char str[maxn], newS[maxn * 2];
int pArr[maxn * 2], newLen;
void getnew()
{
int index = 2;
int len = strlen(str);
newS[0] = '@';
newS[1] = '#';
for (int i = 0; i < len; i++){
newS[index++] = str[i];
newS[index++] = '#';
}//for
newS[index] = '\0';
newLen = index;
}
int mancher()
{
int C = -1, R = -1; //C 表示中心点,R表示右边界
int Max = -1;
for (int i = 1; i < newLen; i++){
pArr[i] = R > i ? min(pArr[2 * C - i], R - i) : 1;
while (newS[i - pArr[i]] == newS[i + pArr[i]]){
pArr[i]++;
}
if (i + pArr[i] > R){ //更新R 与 C
R = i + pArr[i];
C = i;
}
Max = max(Max, pArr[i]);
}//for
return Max - 1; //返回的是原字符串中回文串的长度
}
int main()
{
while (scanf("%s", str) != EOF){
getnew();
printf("%d\n", mancher());
}//while
return 0;
}
从代码可以看到,我们用
while (newS[i - pArr[i]] == newS[i + pArr[i]]){
pArr[i]++;
}
来代替了马拉车算法的4种情况。
Max - 1 恰好是最长回文串的长度。
其他解法:
中心扩展法:
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#include<climits>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
//const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 102
//#define Mod 10000
using namespace std;
const int maxn = 110000 + 5;
char str[maxn], newS[maxn * 2];
int pArr[maxn * 2], newLen;
void getnew()
{
int index = 2;
int len = strlen(str);
newS[0] = '@';
newS[1] = '#';
for (int i = 0; i < len; i++){
newS[index++] = str[i];
newS[index++] = '#';
}//for
newS[index] = '\0';
newLen = index;
}
int expandAroundCenter(int cen)
{
int L, R;
L = R = cen;
while (newS[L] == newS[R]){
L--;
R++;
}
return (R - L - 1) / 2;
}
int main()
{
int ans;
while (scanf("%s", str) != EOF){
ans = -1;
getnew();
for (int i = 1; i <= newLen; i++){
ans = max(ans, expandAroundCenter(i));
}//for
printf("%d\n",ans);
}//while
return 0;
}
getnew()作用如下:
是为了处理长度为偶数的情况的。
或者动态规划法。
Hotaru's problem
题意:给你一个具有n个元素的整数序列,问你是否存在这样一个子序列,该子序列分为三部分,第一部分与第三部分相同,第二部分与第一部分对称(例如2 3 4 4 3 2 2 3 4 显而易见 2 3 4 与 4 3 2 对称,而绿色 部分与 蓝色部分相同。形如所述的最长连续子序列。
题解:用马拉车,求出偶数长度的回文串长度。再求x。
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#include<climits>
#include<functional>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
//const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 102
//#define Mod 10000
using namespace std;
const int maxn = 100000 + 5;
int str[maxn], newS[maxn * 2];
int pArr[maxn * 2], newLen, sLen;
void getnew()
{
int cur = 2;
newS[0] = -2;
newS[1] = -1;
for (int i = 1; i <= sLen; i++){
newS[cur++] = str[i];
newS[cur++] = -1;
}//for
newS[cur] = -3;
newLen = cur - 1;
}
void mancher()
{
int C, R;
C = R = -1;
for (int i = 1; i <= newLen; i++){
pArr[i] = i > R ? 1 : min(pArr[2 * C - i],R - i + 1);
while (newS[i + pArr[i]] == newS[i - pArr[i]]){
pArr[i]++;
}//while
if (i + pArr[i] - 1 > R){
R = i + pArr[i] - 1;
C = i;
}
}//for
/*for (int i = 1; i <= newLen; i++){
cout << newS[i] << " ";
}
cout << endl;
for (int i = 1; i <= newLen; i++){
cout << pArr[i] << " ";
}*/
}
int main()
{
int T, ans = 0, MaxSpace;
int k = 1;
scanf("%d", &T);
while (T--){
memset(pArr, 0, sizeof(pArr));
ans = 0;
scanf("%d", &sLen);
for (int i = 1; i <= sLen; i++){
scanf("%d", &str[i]);
}//for
getnew();
mancher();
for (int i = 3; i <= newLen; i += 2){
if (pArr[i] - 1 > ans){
MaxSpace = pArr[i] - 1;
while (MaxSpace > ans && pArr[i + MaxSpace] < MaxSpace){
MaxSpace--;
}//while
ans = max(ans, MaxSpace);
}//if
}//for
printf("Case #%d: %d\n", k++, ans / 2 * 3); //除2是因为ans用的是newS
}//while
return 0;
}