目录
字符串算法是一种用于解决字符串相关问题的算法。其中,KMP算法和Manacher算法是两种常见的字符串算法,也分别用于解决是非常匹配和最长回文子串问题。是蓝桥杯常考察的点。
1、KMP算法
KMP算法是一种字符串匹配算法,用于在一个字符串中查找另外一个字符串出现的位置。它的基本思想是通过对模式字符串进行预处理,构造一个pnext数组来指导匹配过程中的跳转问题,时间复杂度可以优化到近似到O(n+m)。而基础的暴力算法每次的跳转都是1,因而复杂度最差会达到O(nm)。
KMP算法的主要步骤如下:
a.预处理模式串,构造pnext数组。
b.从文本串的第一个字符开始匹配,逐个字符的和模式串进行匹配。
c.如果匹配成功,继续匹配下一个字符,直到模式串匹配完成。
d.如果匹配失败,利用pnext数组进行跳转,重新匹配。
1.1C++代码模板 洛谷KMP板子题
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的pnext数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && p[i] != p[j + 1]) j = pnext[j];
if (p[i] == p[j + 1]) j ++ ;
pnext[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = pnext[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
j = pnext[j];
// 匹配成功后的逻辑
}
}
1.2Python代码模板 蓝桥KMP题1 蓝桥KMP题2
p.insert(0,0)
s.insert(0,0)
pnext = [0]*(len(p)+10)
i,j = 2,0
while i<=m:
while j and p[i]!=p[j+1]:
j = pnext[j]
if p[i] == p[j+1]:
j+=1
pnext[i] = j
i+=1
i,j=1,0
while i<=n:
while j and s[i]!=p[j+1]:
j = pnext[j]
if s[i] == p[j+1]:
j+=1
if j==m:
print(i-j,end=' ')
j = pnext[j]
i+=1
2、Manacher
Manacher算法是一种用于求最长回文子串的算法,它可以在O(n)的时间复杂度内解决该问题,常规暴力则是枚举每个中点然后拓展附近点算法的时间复杂度为O(n*2)。
Manacher算法的基本思想是利用回文串的对称性质,在求解的过程中对已经匹配过的位置进行存储和复用从而避免了大量的重复技术。
主要步骤如下:
a.对原字符串进行预处理,构造一个新的字符串,使得每个字符串之间都有一个特殊字符(例如"#")隔开,这样确保新字符串的每个回文串都是奇数长度的。
b.维护一个回文半径数组p,其中p[i]表示以i为中心的回文串的半径长度。
c.维护一个右边界r和中心点mid,分别表示已知的最长回文串的右边界和中心点。
d.遍历新字符串,根据已知的回文串的对称性质来更新p[i]的值,以及r和mid的值。
e.遍历p数组,找出其中的最大值,即为原字符串的最长回文子串的长度。
Case1:mid < i < r
由于[ mid - r,mid + r] 是回文的,设j为i以mid为中点的对称点,根据对称性,则我们可以得到:[ j - pj,j + pj ] = [ i -pi,i + pi ] 。 但是 i + pi > r 的时候,我们无法保证上述情况相等(设r以mid)为中心的对称点为x,因为j - pj < x,所以无法保证相等。所以在case1的情况下我们可以直接让 p[ i ] = min (p[ j ],r - i +1)。
Case2:i >= r
这个时候我们不能求出对称点,直接暴力拓展。p[ i ] = 1.
1、C++模板 洛谷 Manacher板子题
void build() {
scanf("%s",c+1);
n=strlen(c + 1);
s[++ cnt]='~';
s[++ cnt]='#';
for(int i=1;i<=n;i++){
s[++cnt] = c[i];
s[++cnt] = '#';
}
s[++ cnt] = '!';
}
void solve() {
for(int i=2;i<=cnt-1;i++){
if(i <= r)
p[i] = min(p[mid * 2 - i], r - i + 1);
else
p[i] = 1;
while(s[i - p[i]] == s[i + p[i]]) ++ p[i];
if(i + p[i] > r)
r = i + p[i] - 1, mid = i;
res = max(res, p[i]);
}
printf("%d",res-1);
}
2、Python模板 蓝桥最长回文子串
s=input()
new_s="#".join([x for x in s])
new_s=" ~#"+new_s+"#!"
mid,r,res,cnt = 0,0,0,len(new_s)
p=[0]*(1000010)
for i in range(2,len(new_s)-1):
if i<r:
p[i]=min(p[mid*2-i],r-i+1)
else:
p[i]=1
while new_s[i-p[i]]==new_s[i+p[i]]:
p[i]+=1
if i+p[i]>r:
mid=i
r=i+p[i]-1
res = max(res,p[i])
print(res-1)