- 回文串的概念:正序和逆序是一致的,比如字符串
abcdcba
- 既然是字符串就会存在奇数长度和偶数长度的回文串
奇回文串:abcdcba
偶回文串:123321
- 实现方法:
(1)暴力的扩展——时间复杂度O(n^2)
遍历每一个字符,从这个字符开始向两边扩展并且比较是否一致,记录每个字符可以遍历到的最大长度。比如abcdcba
,当遍历到字符d的时候就是最长的回文串。
但是没有办法解决偶数长度的串,比如123321
是没有办法从中间向两边扩展的,因为中轴是虚拟的,所以可以预先处理一下给定的字符串,让虚轴显现。
方法:在字符串的开头、两两字符之间、结尾都添加特殊字符#,当然也可以是串内字符,因为添加的字符只会和特殊字符进行匹配,都是关于自身来实现对称的
比如:
偶回文串:123321, 预处理后:#1#2#3#3#2#1#,这样就显现了虚轴
奇回文串:aba, 预处理后:#a#b#a#
这样从第一个字符需要比较1次,第二个2次,一直到中间,再对称,两个等差数列。时间复杂度就是一个O(n^2)级别的。
(2)Manacher算法,时间复杂度:O(n)
会用到的关键词:
1、回文半径/回文直径数组arr【】:记录从每个位置开始向两边扩展可以到达的最边界
2、最右回文边界pR:顾名思义就是回文串的最右边界,从0位置开始
3、最右回文半径的回文中心C:回文串的中心位置,取决于pR,当pR不动则不变,否则更新
情况共有四种,分为两类
1、pos位置的字符在最右回文边界pR的右边,只能直接暴力扩展
2、pos位置的字符在最右回文边界pR的左边,分为三种情况:
2.1、pos位置关于回文中心C的对称点pos’的回文半径彻底在回文直径(L,R)内
C是回文中心,由于已经扩展到(L,R)
∴ m = q
n = p
但是pos'的回文区域(L',R')完全在(L,R)之间,
∴ m ≠ n
则 p ≠ q 所以不需要扩再进行验证
========================================================
2.2、pos位置关于回文中心C的对称点pos’的回文半径部分在回文直径(L,R)外
C是回文中心,回文直径扩展到(L,R)
∴ q ≠ p
y = x
而pos'的回文半径是(L',R')则
q = y
∴ x ≠ p
∴ 所以R-pos之后不需要扩再进行验证
========================================================
2.3、pos位置关于回文中心C的对称点pos’的回文半径恰好压线(左边界重合)
由图可知:
y = m
x ≠ n
x ≠ y
则x ≠ m 但是无法确定m和n的关系,所以需要继续验证
综上就是所有的情况
- 代码
/*
*最右回文边界pR
*回文中心C
*回文半径数组pArr[]--记录从左向右扩pR的每一个位置回文半径
*/
/**
*case1:字符在回文边界pR的右边--暴力扩
*case2:i'的(区域)在L内--不扩
*case3:i'的(区域)一部分在L内--不扩
*case4:i'的(区域)压线--扩
*/
#include<bits/stdc++.h>
using namespace std;
vector<char> GetManacherString(char str[])
{
int len = strlen(str);
vector<char> res(len*2+1);
int index = 0;
for (int i = 0; i < res.size(); ++i)
{
res[i] = (i&1) == 0 ? '#' : str[index++];
}
return res;
}
//最长的回文子串
int MaxLcpsLength(char str[])
{
int len = strlen(str);
if(len == 0 || str == NULL) return 0;
std::vector<char> ManacherString = GetManacherString(str);
int pR = -1; //最右回文边界
int C = -1; //最右回文中心
int maxn = INT_MIN; //最大回文半径
int str_Center = -1;//最长回文串回文中心
int ManacherString_len = ManacherString.size();
std::vector<int> pArr(ManacherString_len,0);
for (int i = 0; i < ManacherString_len; ++i)
{
//字符在pR的里面(case2、case3)
pArr[i] = pR > i ? min(pArr[2*C-i],pR - i) : 1; //获取对称点i'的pArr[i']
while(i+pArr[i] < ManacherString_len && i - pArr[i] > -1) //越界检查
{
if(ManacherString[i+pArr[i]] == ManacherString[i-pArr[i]]){ //R>i case2和case3不进入 直接break
pArr[i]++;
}else{
break;
}
}
if(i+pArr[i] > pR){ //字符在pR的外面 case1
pR = i+pArr[i];
C = i;
}
if(pArr[i] > maxn)
{
maxn = pArr[i];
str_Center = C;
}
}
int Lastmaxn = maxn-1;
//cout<<"Right Center is:"<<C<<endl;
//cout<<"Center is:"<<str_Center<<endl;
//求取子串的内容:如下利用函数substr((str_Center-maxn-1)/2, maxn);
string newstr = str;
cout<<"newstr is:"<<newstr<<endl;
cout<<"MaxLcpString is:"<<newstr.substr((str_Center-Lastmaxn)/2, Lastmaxn)<<endl;
return Lastmaxn;
}
int main(int argc, char const *argv[])
{
char str[] = "abc1234321ab";
cout<<"oldstr is:"<<str<<endl;
cout<<"MaxLcpsLength is: "<<MaxLcpsLength(str)<<endl;
return 0;
}
结果: