这里写自定义目录标题
- 马拉车算法
- 剑指 Offer II 020. 回文子字符串的个数
马拉车算法
马拉车算法可以以接近线性时间判断计算回文串长度,遍历每一个中心点,再向两遍扩充
- 填充字符
其中$ ! 作为边界,添加#可以避开对偶数回文串的讨论,所有回文串都是奇数回文串
原字符串 | 填充后字符串 |
---|---|
abba | $#a#b#b#a#! |
aba | $#a#b#a#! |
当考虑以
i
i
i为中心点的字符串时,
f
[
i
]
f[i]
f[i]为以
i
i
i为中心点的回文串的半径,
R
m
a
x
Rmax
Rmax为当前回文串的最大右端点,
N
o
d
e
Node
Node为相应中心点,
i
i
i和
j
j
j是以
N
o
d
e
Node
Node为中心点的回文串中对应部分
-
初始化
利用回文串的特性进行初始化, i i i和 j j j(也就是图中蓝色方块)是相等的,所以可以直接用 f [ j ] f[j] f[j]初始化 f [ i ] f[i] f[i]
j = 2 ∗ n o d e − i j=2*node-i j=2∗node−i
初始化(要考虑边界情况)
f [ i ] = ( i < R m a x ) ? m i n ( f [ j ] , R m a x − i + 1 ) : 1 f[i]=(i<Rmax)?min(f[j],Rmax-i+1):1 f[i]=(i<Rmax)?min(f[j],Rmax−i+1):1 -
向外扩展
继续向两边判断是否还有相等的字符 -
维护Rmax和Node
-
一些细节:
回文串的半径为 f [ i ] − 1 f[i]-1 f[i]−1 因为在匹配中一定是停止于#符号 例如#a#b#b#a# 所以半径减1,或者说 f [ i ] f[i] f[i]的半径中包括了中心点和最后的#号,在上面的例子中半径为 #b#a#
求回文串个数时要除以2,因为有#号
剑指 Offer II 020. 回文子字符串的个数
class Solution {
public:
int countSubstrings(string s) {
string t="$#";
int f[2005],rmax=1,node=1,ans=0;
int i;
int n=s.length();
for(i=0;i<n;i++){
t+=s[i];
t+='#';
}
t+='!';
int m=t.length();
// cout<<t<<endl;
for(i=1;i<m-1;i++){
f[i]=(i<=rmax)?min(f[2*node-i],rmax-i+1):1;
while(t[i-f[i]]==t[i+f[i]])f[i]++;
if(rmax<i+f[i]-1){
rmax=i+f[i]-1;
node=i;
}
ans+=f[i]/2;
}
return ans;
}
};