今天对字符串有了一点新的理解,遂再写一篇博客。
注:字符串默认从 0 0 0 开始编号。
可以参考 我之前写的博客。不再赘述。下文用长度表示。
( S + c ) ≡ ( S m o d P ) + c ( m o d P ) (S+c) \equiv (S \bmod P) + c \pmod P (S+c)≡(SmodP)+c(modP)
定义 g o ( i , c ) go(i, c) go(i,c) 函数,表示 P P P 的 i i i 前缀添加字符 c c c 的字符串的模 P P P 值。定义失配函数(fail) f i f_i fi 表示 P P P 的 i i i 前缀时增加的字符 c c c 失配的模 P P P 值。
g o ( i , c ) = { i + 1 P i = c g o ( f i , c ) P i ≠ c go(i, c) = \begin{cases} i+1 & P_i =c\\ go(f_i,c) & P_i \ne c \end{cases} go(i,c)={i+1go(fi,c)Pi=cPi=c
int go(int i, char c)
{
if(P[i] == c) return i + 1;
if(i == 0) return 0;
return go(f[i], c);
}
...
f[1] = 0;
for(int i=1; i<P.size(); i++)
f[i+1] = go(f[i], c);
关于 f i f_i fi,一方面从自动机角度考虑,即前缀函数 π i \pi_i πi。
一方面从字符串取模的角度考虑, f i = P [ 1.. i ) f_i=P[1..i) fi=P[1..i) 模 P P P 值。 P [ 0.. f i ) P[0..f_i) P[0..fi) 模 P P P 均为本身,根据定义, f i f_i fi 同样满足 π i \pi_i πi 的性质。
int cur = 0;
for(int i=0; i<text.size(); i++)
{
cur = go(cur, text[i]);
...
}
将
g
o
go
go 函数视为开车,
f
i
f_i
fi 视为汽油。cur
表示当前油量。
因为 f i < i f_i < i fi<i,所以开车会消耗汽油, x x x 升汽油最多开车 x x x 公里。每次增加文本字符,开车或者加 1 1 1 升油。显然,加了多少油才能跑多少次,初始为 0 0 0。由此,时间复杂度为线性 。
求字符串前缀出现次数:
设 t i t_i ti 表示长度为 i i i 的前缀出现次数。 t i = 1 + ∑ f j = i t j t_i = 1 + \sum_{f_j=i} t_j ti=1+∑fj=itj。可通过从后向前递推实现这一过程。
问题形如出现某个模式串 P P P 的期望长度。
由于只关心匹配,考虑只关心模 P P P 的值,据此结合待定系数法做期望 dp 即可。
题解
https://blog.csdn.net/weixin_73113801/article/details/133465423
如题,清除字符串中的模式串 P P P。
逐位枚举,考虑当前字符串模 P P P 的值即可。