题目
给定字符串str,若子串s是回文串,称s为str的回文字串。设计算法,计算str的最长回文字串。
枚举所有字串,显然是一种解法,但效率太过低下。
算法解析
- 因为回文串有奇数和偶数的不同,判断一个串是否是回文串,往往要分开编写,造成代码拖沓。
- 一个简单的事实:缠度为n的字符串,共有n-1个“邻接”,加上首字符的前面,和末字符的后面,共n+1的“空”。因此,字符串本身和“空”一起,共有2n+1个,必定是奇数。如abbc-〉#a#b#c#;aba-〉#a#b#a#
- 因此,将带计算母串扩展成gap串,计算回文字串的过程中,只考虑奇数匹配即可。
- 字符串12212321-〉s[]="$#1#2#2#1#2#3#2#1#";为了处理统一,最前面加一位未出现的字符,如$
- 用一个数组P[i]来记录以字符S[i]为中心的最长回文字串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:
- S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
- P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
- P[i]-1正好是原字符串中回文串的总长度
下面介绍一下回文算法的核心算法思想Manacher算法
如下图
- 这里需要求的是i的回文字串的个数,mx为目前匹配的最远的位置,为0到i-1中k+P[k]最大的数,代码里有体现,现在已知的是i以前的所有回文字串是求出来的。
- 记i关于id的对称点位j(=2*id-i),若此时满足条件mx-i〉P[j];则有如下:
- 记my为mx关于id的对称点(my=2*id-mx);
- 由于以S[id]为中心的最大回文子串为S[my+1...id...mx-1],即:S[my+1,...,id]与S[id,...,mx-1]对称,而i和j关于id对称,因此P[i]=P[j](P[j]是已知的)。
- 记关于id的对称点为j(=2*id-i),若此时满足条件mx-i〈P[j],则有如下:
- 记my为mx关于id的对称点(my=2*id-mx);
- 由于以S[id]为中心的最大回文子串为S[my+1...id...mx-1],即:S[my+1,...,id]与S[id,...,mx-1]对称,而i和j关于id对称,因此P[i]至少等于mx-i(途中绿色框部分)。
代码如下所示
void Manacher(char* s, int* P)
{
int size = strlen(s);
P[0] = 1;
int id = 0;
int mx = 1;
for (int i = 1; i < size; i++)
{
if (mx > i)//mx比i大,说明i被mx控制住了
{
P[i] = P[2*id - i] ? P[2*id - i] < (mx - i);
}
else
{
P[i] = 1;
}
for (; s[i + P[i]] == s[i - P[i]]; P[i]++);
if (mx < i + P[i])
{
mx = i + P[i];
id = i;
}
}
}