前缀函数
在oi-wiki上学习了前缀函数,自己也总结一下。
定义:
p
i
[
i
]
=
m
a
x
(
j
)
,
s
[
0..
j
]
=
s
[
i
−
j
+
1
,
i
]
pi[i]=max(j),s[0..j]=s[i-j+1,i]
pi[i]=max(j),s[0..j]=s[i−j+1,i]
O(n)计算方法:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
int p[maxn];
char s[maxn];
void getpi(int n)
{
p[0]=0;
for(int i=1;i<n;i++)
{
int j=p[i-1];
while(j&&s[j]!=s[i]) j=p[j-1];
if(s[i]==s[j]) j++;
p[i]=j;
}
}
应用:
- 在字符串中查找:在文本t中查找s,构造字符串s+#+t,求前缀函数,统计t中p[i]=|s|的数目。
- 字符串的周期:字符串长度一定要是整数周期倍,n-p[n-1]为最小周期。
- 统计前缀出现次数,若相同字符串,对于每个i,长度为p[i],p[p[i]-1]…的前缀都出现了,且后端点为i,统计即可。若要统计s的前缀在t中出现次数,则需构造s+#+t来做。
- 一个字符串中本质不同的串:一个字符一个字符处理,当前已处理字符串为s,新增字符c,构造t=~(s+c)(翻转),计算前缀函数,新增子串数量|s|+1-max(p[i]).
利用前缀函数构造自动机:待补
z函数
定义:z函数为n个数,z[i]代表以s[i]开头且是s的一个前缀的最长字符串长度。
最优求解算法和马拉车的求解有点像:
void getz(int n)
{
int l=0,r=0;
for(int i=1;i<n;i++)
{
if(i<=r) z[i]=z[i-l];
while(i+z[i]<n&&s[i+z[i]]==s[z[i]]) z[i]++;
if(i+z[i]-1>r)
{
l=i,r=i+z[i]-1;
}
}
}