如何在O(n)时间复杂度内找到一个字符串的最长回文前缀?
a
是您的字符串,则让ha[x]
成为从左到右计算出a
中前x
个字符的哈希值,让hr[x]
成为从右到左计算出s
中前x
个字符的哈希值。您需要找到最后一个位置i
,使得hf[i] = hb[i]
。int match = n - 1;
int ha1 = 0, ha2 = 0, hr1 = 0, hr2 = 0;
int m1 = 1, m2 = 1;
for ( int i = 0; a[i]; ++i )
{
ha1 = (ha1 + m1*a[i]) % mod1;
ha2 = (ha2 + m2*a[i]) % mod2;
hr1 = (a[i] + base1*hr1) % mod1;
hr2 = (a[i] + base2*hr2) % mod2;
m1 *= base1, m1 %= mod1;
m2 *= base2, m2 %= mod2;
if ( ha1 == hr1 && ha2 == hr2 )
match = i;
}
i
从左到右,你只访问 a[i]
). - Andre Holzneri
只从左到右移动。在每一步 i
,您将在 match
中存储当前最长回文前缀的长度。只有滚动哈希会双向移动。 - IVladha1
、ha2
、hr1
或hr2
中的任何一个是如何从右到左计算的。在每次循环迭代中,只处理下一个元素(a[i]
)的右侧。 - Andre Holznerha1
和hr1
。在i = 0
时,我们有ha1 = a [0]
和hr1 = a [0]
。如果两者相等(显然它们会),我们就有了一个长度为1的回文前缀。在i = 1
时,我们有ha1 = a [0] + base1 * a [1]
和hr1 = a [1] + a [0] * base1
。现在你看到这是怎么回事了吗?尝试为i = 2
构建ha1
和hr1
。@Nikita Rybak - 是的,没有保证。 - IVlad使用z算法(https://codeforces.com/blog/entry/3107)。假设s是给定的长度为m的字符串。代码:
string rev="",str=s;
int m=s.size(),longestPalindromicPrefix=1;
if(m==0 || m==1) longestPalindromicPrefix=m;
for(int i=m-1;i>=0;i--)
rev+=s[i];
s+='#';
s+=rev;
int n=s.size(),z[n+4],l=0,r=0;
for(int i=1;i<n;i++){
if(i>r){
l=r=i;
while(r<n && s[r-l]==s[r])
r++;
z[i]=r-l,r--;
}
else{
int k=i-l;
if(z[k]<r-i+1)
z[i]=z[k];
else{
l=i;
while(r<n && s[r-l]==s[r])
r++;
z[i]=r-l,r--;
}
}
}
for(int i=m+1;i<n;i++){
if(2*z[i]>=2*m-i && z[i]>longestPalindromicPrefix)
longestPalindromicPrefix=z[i];
}
解决更一般的问题,不是前缀而是子字符串,在O(n)中:
http://www.akalin.cx/2007/11/28/finding-the-longest-palindromic-substring-in-linear-time/
在谷歌上搜索“最长回文前缀”的第二个结果...
或者使用后缀树的解决方案:
fastLongestPalindromes(..)
函数实际上是最坏情况下的 O(n)
复杂度吗?我看到了两个嵌套循环... - Andre Holzner