HDU 3068 最长回文
题目:
- 一个字符串,问最大回文串的长度。
思路:
- 字符串长度范围在110000以内,所以用动态规划O(n ^ 2)和中心扩展算法O(n ^ 2)会超时。所以我们用更快的Manacher算法O(n)。
Manacher Algorithm【O(n)】
- 为了避免字符串 s 长度奇数偶数的讨论,我们在字符串两两字符间插入特殊字符‘#’,然后再在首位置插入特殊字符‘$’,形成新的字符串 t .
- 引入数组radius[ i ]:表示以 i 为中心的最长回文子串的半径。例如:“abcba”,radius[ 2 ] = 3.
- 引入mx:表示回文子串能延伸到的最右端的位置(回文子串不包括mx),mx更新:mx = radius[ i ] + i .
- 引入id:能延伸到最右端的那个回文串的中心
- 定义Center,Len:表示字符串 t 中,当前位置之前最长回文串的中心和以Center为中心的回文串的半径
最后的答案: - 最长回文子串的起始位置 start = ( Center - Len ) / 2
- 长度:Len - 1
- 也就是:s.substr((Center - Len) / 2, Len - 1) .
radius[ i ]的更新
- 第一种情况: 如果mx - i > radius[ (id << 1) - i ],那么说明以 t [ (id << 1) - i ]为中心的回文子串含于在以 t [ id ]为中心的回文子串中
- 第二种情况: 否则,说明 t [ (id << 1) - i ]为中心的回文子串可能不完全含于在以 t [ id ]为中心的回文子串中
- 因为关于 t [ id ]为中心的回文子串是关于id左右对称的。所以,在第一种情况下,以t [ i ]为中心的回文子串一定和以t [ (id << 1) - i ]为中心的回文子串相同,这时候radius[ i ] = radius[ (id << 1) - i ]
- 而在第二种情况下,那么只能保证以t [ i ]为中心,mx - i 为半径的这一段子串,也就是和以t [ (id << 1) - i ]为中心的回文子串在 以t [ id ]为中心的回文子串中的那一部分相等,这时候radius[ i ] = mx - 1
- 而当mx <= i 的时候,radius[ i ] = 1 .
- 也就是: radius[i] = mx > i ? min(radius[id * 2 - i], mx - i) : 1;
题目AC_Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxN = 11e4 + 5;
int Manacher(string &s)
{
int len = s.size();
string t = "$#";
for(int i = 0; i < len ; i ++ )
{
t += s[i];
t += "#";
}
len = t.size();
vector<int> radius(len, 0);
int mx = 0, id = 0;
int Len = 0, Center = 0;
for(int i = 1; i < len; i ++ )//这里从1开始是因为我们字符串 t 的第一个字符是无效特殊字符'$'
{
radius[i] = mx > i ? min(radius[id * 2 - i], mx - i) : 1;
while(i + radius[i] < len && i - radius[i] >=0 && t[i + radius[i]] == t[i - radius[i]])
++ radius[i];
if(radius[i] > Len)
{
Center = i;
Len = radius[i];
}
if(mx < i + radius[i])
{
mx = radius[i] + i;
id = i;
}
}
return Len - 1;
}
int main()
{
string s;
while(cin >> s)
cout << Manacher(s) << endl;
return 0;
}
输出最长回文串_Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxN = 11e4 + 5;
string Manacher(string &s)
{
int len = s.size();
string t = "$#";
for(int i = 0; i < len ; i ++ )
{
t += s[i];
t += "#";
}
len = t.size();
vector<int>radius(len, 0);
int mx = 0, id = 0, Len = 0, Center = 0;
for(int i = 1; i < len; i ++ )
{
radius[i] = mx > i ? min(mx - i, radius[(id << 1) - i]) : 1;
while(i + radius[i] < len && i - radius[i] >= 0 && t[i + radius[i]] == t[i - radius[i]])
++ radius[i];
if(mx < i + radius[i])
{
mx = i + radius[i];
id = i;
}
if(Len < radius[i])
{
Len = radius[i];
Center = i;
}
}
return s.substr((Center - Len) / 2, Len - 1);
}
int main()
{
string s;
while(cin >> s)
cout << Manacher(s) << endl;
return 0;
}