字符串——字符串匹配

article/2025/10/4 0:07:45

文章目录

  • 字符串匹配
    • 一、基本概念
      • 字符串匹配问题
      • 符号和术语
      • 后缀重叠引理
    • 二、朴素字符串匹配算法
    • 三、Rabin-Karp算法(字符串Hash算法)
      • 进制Hash
      • 质数Hash
    • 四、利用有限自动机进行字符串匹配
      • 有限自动机
      • 字符串匹配自动机
      • 计算状态转移函数
      • 代码
    • 五、Knuth-Morris-Pratt算法
      • 模式的前缀函数
      • 代码
    • 六、Z-函数(扩展KMP)


字符串匹配

一、基本概念

字符串匹配问题

假设文本是一个长度为 n n n的数组 T [ 1 … n ] T[1 \ldots n] T[1n],而模式是一个长度为 m m m的数组 P [ 1 … m ] P[1 \ldots m] P[1m],其中 m ≤ n m \leq n mn,进一步假设 P P P T T T的元素都是来自一个有限的字母集 ∑ \sum 的字符。数组 T T T P P P通常被称为字符串

如果 0 ≤ s ≤ n − m 0 \leq s \leq n-m 0snm,并且 T [ s + 1 … s + m ] = P [ 1 … m ] T[s+1 \ldots s+m]=P[1 \ldots m] T[s+1s+m]=P[1m],那么称模式 P P P在文本 T T T中出现过,且偏移为 s s s。如果 P P P T T T中以偏移 s s s出现,那么称 s s s有效偏移,否则为无效偏移

字符串匹配问题就是在 T T T中找到 P P P的所有有效偏移 s s s

符号和术语

  1. 字符串集合:我们用 ∑ ∗ \sum^{*} 来表示包含所有有限长度的字符串的集合,该字符串是由字母表 ∑ \sum 中的字符串组成。在本文中,我们只考虑有限长度的字符串。长度为 0 0 0的字符串用 ϵ \epsilon ϵ表示,也属于 ∑ ∗ \sum^{*}
  2. 字符串的长度:一个字符串 T T T的长度用 ∣ T ∣ |T| T表示。特别的, ∣ ϵ ∣ = 0 |\epsilon| = 0 ϵ=0
  3. 连结:两个字符串 x x x y y y的连结表示为 x y xy xy,意义为 x x x后接 y y y,长度为 ∣ x ∣ + ∣ y ∣ |x| + |y| x+y
  4. 前缀:如果对于三个个字符串 x = w y x=wy x=wy,那么称字符串 w w w x x x的前缀,记作 w ⊏ x w \sqsubset x wx,且 ∣ w ∣ ≤ ∣ x ∣ |w| \leq |x| wx
  5. 后缀:如果对于三个个字符串 x = y w x=yw x=yw,那么称字符串 w w w x x x的后缀,记作 w ⊐ x w \sqsupset x wx,且 ∣ w ∣ ≤ ∣ x ∣ |w| \leq |x| wx。特别的,空字符串 ϵ \epsilon ϵ是任意一个字符串的前缀和后缀。

后缀重叠引理

假设 x x x y y y z z z满足 x ⊐ z x \sqsupset z xz y ⊐ z y \sqsupset z yz。如果 ∣ x ∣ ≤ ∣ y ∣ |x| \leq |y| xy,那么 x ⊐ y x \sqsupset y xy,反过来如果 ∣ y ∣ ≤ ∣ x ∣ |y| \leq |x| yx,那么 y ⊐ x y \sqsupset x yx。如果 ∣ x ∣ = ∣ y ∣ |x| = |y| x=y,那么 x = y x = y x=y

为了符号简洁,我们把模式 P [ 1 … m ] P[1 \ldots m] P[1m]的由 k k k个字符串组成的的前缀 P [ 1 … k ] P[1 \ldots k] P[1k],记作 P k P_{k} Pk。因此 P 0 = ϵ , P m = P = P [ 1 … m ] P_{0} = \epsilon, P_{m}=P=P[1 \ldots m] P0=ϵ,Pm=P=P[1m]。与此类似,我们把文本 T T T中由 k k k个字符组成的前缀记作 T k T_{k} Tk

字符串匹配问题转化为:找到所有的偏移 s ( 0 ≤ s ≤ n − m ) s(0 \leq s \leq n-m) s(0snm),使得 P ⊐ T s + m P \sqsupset T_{s+m} PTs+m


二、朴素字符串匹配算法

朴素字符串算法是通过一个循环找到所有的有效偏移,该循环对 n − m + 1 n-m+1 nm+1个可能的 s s s值进行检测,看是否满足条件 P [ 1 … m ] = T [ s + 1 … s + m ] P[1 \ldots m]=T[s+1 \ldots s+m] P[1m]=T[s+1s+m]

NAIVE-STRING-MATCHER(T,P):n = T.lengthm = P.lengthfor s = 0 to n-m:if P[1...m] = T[s+1 ... s+m]:print "Pattern occurs whit shift",s

伪代码中的循环枚举 s s s的位置,for中的判断隐含了一个循环,这个循环检测偏移 s s s是否为有效偏移。

在最坏情况下,朴素算法的运行时间为 O ( ( n − m + 1 ) m ) O((n-m+1)m) O((nm+1)m)。如果 m = n / 2 m = n/2 m=n/2,则运行时间为 Θ ( n 2 ) \Theta(n^{2}) Θ(n2)

我们看到,朴素字符串匹配算法不是解决该问题的最佳过程。是因为当其他无效的 s s s值存在时,他只关心一个有效的 s s s的值,而完全忽略了检测无效的 s s s值的时候所带来的有用信息。例如对于 P = a a a b P=aaab P=aaab并且我们发现 s = 0 s=0 s=0是一个有效的偏移值,由于 T [ 4 ] = b T[4] = b T[4]=b,所以 s = 1 , 2 , 3 s=1,2,3 s=1,2,3都不是有效的。

三、Rabin-Karp算法(字符串Hash算法)

进制Hash

Rabin-Karp算法运用了初等数论的知识,其实就是对字符串进行 H a s h Hash Hash运算,比较每一位上的 H a s h Hash Hash值是否相等即可。

为了便于说明,我们假设 ∑ = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } \sum = \{0,1,2,3,4,5,6,7,8,9\} ={0,1,2,3,4,5,6,7,8,9},这样每个字符都是十进制数字。(在通常情况下,可以假定每个字符都是以 d d d为基数表示的数字,其中 ∣ ∑ ∣ = d |\sum| = d =d),每一个字符串就是相当与一个十进制数,其长度为 k k k

给定一个模式 P [ 1 … m ] P[1 \ldots m] P[1m],令 p p p为表示的十进制数。类似的,有字符串 T [ 1 … n ] T[1 \ldots n] T[1n],假设 t s t_{s} ts表示子串 T [ s + 1 … s + m ] T[s+1 \ldots s+m] T[s+1s+m]对应的十进制数。当然,只有当 s s s是一个有效偏移的时候, p = t s p = t_{s} p=ts,如果能在时间 Θ ( m ) \Theta(m) Θ(m)内计算出 p p p的值,并且在总时间 Θ ( n − m + 1 ) \Theta(n-m+1) Θ(nm+1)内计算出所有的 t s t_{s} ts值,那么通过比较 t s t_{s} ts p p p的值就可以在时间 Θ ( m ) + Θ ( n − m + 1 ) = Θ ( n ) \Theta(m) + \Theta(n-m+1) = \Theta(n) Θ(m)+Θ(nm+1)=Θ(n)时间内找到所有的有效偏移。

其实这是一种 H a s h Hash Hash运算,根据霍纳法则,我们可以在规定的时间内计算出 p p p

p = P [ m ] + 10 ( P [ m − 1 ] + 10 ( P [ m − 2 ] + … + 10 ( P [ 2 ] + 10 P [ 1 ] ) … ) ) p = P[m] + 10(P[m-1] + 10(P[m-2] + \ldots + 10(P[2] + 10P[1]) \ldots )) p=P[m]+10(P[m1]+10(P[m2]++10(P[2]+10P[1])))

类似的,我们可以通过规定的时间计算出 t 0 t_{0} t0的值,并且能在 O ( 1 ) O(1) O(1)的时间内利用 t 0 t_{0} t0的值计算出 t 1 t_{1} t1的值,依次类推,总结出递推公式:

t s + 1 = 10 ( t s − 1 0 m − 1 T [ s + 1 ] ) + T [ s + m + 1 ] t_{s+1} = 10(t_{s}-10^{m-1}T[s+1])+T[s+m+1] ts+1=10(ts10m1T[s+1])+T[s+m+1]

减去 1 0 m − 1 T [ s + 1 ] 10^{m-1}T[s+1] 10m1T[s+1],表示减去最高位的数字,乘以十表示将这个数向左移动一位,加上 T [ s + m + 1 ] T[s+m+1] T[s+m+1],表示加上最低位。

预先计算出常数 1 0 m − 1 10^{m-1} 10m1,可以使用快速幂计算。

到目前为止,我们就始终在回避一个问题,如果 m m m的值过大,则 t i t_{i} ti的值就可能很大,以至于超过了运算的范围,因此我们就需要运用数论的知识,将其进行取模运算。

选定一个素数 q q q,使得 d q dq dq在一个机器字长内,我们就可以进行取余运算:

t s + 1 = ( d ( t s − h T [ s + 1 ] ) + T [ s + m + 1 ] ) m o d q t_{s+1} = (d(t_{s}-hT[s+1])+T[s+m+1]) \mod q ts+1=(d(tshT[s+1])+T[s+m+1])modq

其中 d d d d d d进制, h h h h ≡ d m − 1 m o d q h \equiv d^{m-1} \mod q hdm1modq

但是这种结果并不是很完美,因为 t s ≡ p m o d q t_{s} \equiv p \mod q tspmodq,并不能说明 t s = p t_{s} = p ts=p,因此,我们还必须仿照朴素算法那样,验证一下这个答案。

如果 q q q是一个素数,并且这个 q q q足够大,这种伪命中点的概率发生的几率很小。

Rabin-Karp算法的期望时间复杂度为 O ( n ) + O ( m ( v + n / q ) ) O(n) + O(m(v+n/q)) O(n)+O(m(v+n/q))。预计较好的情况(有效偏移比较少,素数大, m m m远远小于 n n n)。这个算法的时间复杂度为 O ( n ) O(n) O(n)

还可以使用链接法或开放寻址法来进行无错Hash。

同时,我们可以定义 h [ 0 ] = s [ 0 ] h[0] = s[0] h[0]=s[0] h [ k ] = ( h [ k − 1 ] A + s [ k ] ) m o d B h[k] = (h[k-1]A + s[k]) \mod B h[k]=(h[k1]A+s[k])modB,位权 p [ 0 ] = 1 p[0] = 1 p[0]=1 p [ k ] = ( p [ k − 1 ] A ) m o d B p[k] = (p[k-1]A) \mod B p[k]=(p[k1]A)modB

这样我们就能在 O ( 1 ) O(1) O(1)的时间内计算出某个子串 S [ a … b ] S[a \ldots b] S[ab]的hash值 ( h [ b ] − h [ a − 1 ] p [ b − a + 1 ] ) m o d B (h[b]-h[a-1]p[b-a+1]) \mod B (h[b]h[a1]p[ba+1])modB

质数Hash

如果我们想判断两个单词是不是同体词(即两个字符串每个字母出现的次数都相同,例如eat和ate)。此时我们将每一个字母替换成一个唯一的质数,那么根据算数基本定理,我们就可以把一个字符串看成是一个由多个质数相乘得到的唯一的整数,那么如果两个单词是同体词,那么这个两个Hash值一定相等。

四、利用有限自动机进行字符串匹配

有限自动机

一个有限自动机 M M M是一个五元组 ( Q , q 0 , A , ∑ , δ ) (Q,q_{0},A,\sum,\delta) (Q,q0,A,,δ),其中:

  • Q Q Q是状态的有限集合
  • q 0 ∈ Q q_{0} \in Q q0Q是初始状态
  • A ⊆ Q A \subseteq Q AQ是一个特殊接受状态的集合
  • ∑ \sum 是有限输入字母表
  • δ \delta δ是一个从 Q × ∑ Q \times \sum Q× Q Q Q的函数,称为 M M M的转移函数

有限自动机状态开始于 q 0 q_{0} q0,读入一个字符 a a a,则他从状态 q q q变成了状态 δ ( q , a ) \delta(q,a) δ(q,a)。当自动机的状态属于 Q Q Q的时候,我们说自动机接受了迄今为止所有的字符。

有限自动机 M M M引入一个终态函数 ϕ ( w ) \phi(w) ϕ(w),他是一个从 ∑ ∗ \sum^{*} Q Q Q的一个函数,函数的值为自动机 M M M处理完字符串 w w w之后所处的最终状态。因此当且仅当 ϕ ( w ) ∈ A \phi(w) \in A ϕ(w)A的时候, M M M接受字符串 w w w。存在递归定义:

ϕ ( ϵ ) = q 0 ϕ ( w a ) = δ ( ϕ ( w ) , a ) \phi(\epsilon)=q_{0} \\ \phi(wa) = \delta(\phi(w),a) ϕ(ϵ)=q0ϕ(wa)=δ(ϕ(w),a)

字符串匹配自动机

对于给定一个模式 P P P,我们可以进行预处理构造一个有限状态自动机,之后,对于字符串 T T T,就可以在 O ( n ) O(n) O(n)的时间内求出所有的有效偏移。

为了构造自动机,我们要先定义后缀函数 σ ( x ) \sigma(x) σ(x),函数是一个从 ∑ ∗ \sum^{*} { 0 , 1 , 2 , 3 , … , m } 的 映 射 函 数 \{0,1,2,3,\ldots,m\}的映射函数 {0,1,2,3,,m}

给定模式 P P P,模式 P P P的后缀函数为

σ ( x ) = max ⁡ ( k : P k ⊐ x ) \sigma(x)=\max(k:P_{k} \sqsupset x) σ(x)=max(k:Pkx)

也就是说存在一个最大长度的子串,既是 P P P的前缀又是 x x x的后缀,子串的长度就是后缀函数的值。

给定模式 P [ 1 … m ] P[1 \ldots m] P[1m]对应的字符串匹配自动机为:

  • 状态集合 Q Q Q { 0 , 1 , 2 , 3 , … , m } \{0,1,2,3,\ldots,m\} {0,1,2,3,,m}。开始状态 q 0 q_{0} q0 0 0 0,并且只有状态 m m m是唯一的接受状态。
  • 对于任意的状态 q q q和字符 a a a,转移状态函数 δ ( q , a ) \delta(q,a) δ(q,a)的定义为 δ ( q , a ) = σ ( P q a ) \delta(q,a)=\sigma(P_{q}a) δ(q,a)=σ(Pqa)

也就是说,存在有效偏移 s s s,那么 P P P一定是 T s + m T_{s+m} Ts+m的后缀,状态机的状态就是后缀函数的值。并且存在有效偏移 s s s,当且仅当状态 q = m q=m q=m

状态机维护一个不变式:

ϕ ( T i ) = σ ( T i ) \phi(T_{i}) = \sigma(T_{i}) ϕ(Ti)=σ(Ti)

T i T_{i} Ti表示 T [ 1 … i ] T[1 \ldots i] T[1i]

自动机
图片中描述了模式 P = a b a b a c a P=ababaca P=ababaca的自动机的 D A G DAG DAG模型,除了这个模型,还有二维表模型。

考虑一下两种情况:

  • a = P [ q + 1 ] a=P[q+1] a=P[q+1],此时我们可以放心的把状态转移到 σ ( P q a ) = q + 1 \sigma(P_{q}a)=q+1 σ(Pqa)=q+1,这个状态沿着主轴的方向进行。
  • a ≠ P [ q + 1 ] a \neq P[q+1] a=P[q+1],这时我们必须找到一个更小的子串,确保这个子串满足 σ ( P q a ) \sigma(P_{q}a) σ(Pqa)

此时,构造完自动机,我们就可以根据自动机处理文本 T T T,找到状态 q = m q=m q=m的时候,偏移 i − m i-m im就是有效偏移。

为了证明有限自动机的正确性,我们首先要说明两个引理。

后缀不等式:对于任意字符串 x x x和字符 a a a σ ( x a ) ≤ σ ( x ) + 1 \sigma(xa) \leq \sigma(x) + 1 σ(xa)σ(x)+1

也就是说如果字符 a a a正好是下一个字符,等号成立,如果不是,那么后缀不可能再长了。

后缀函数递归定理:对任意字符串 x x x和字符 a a a,若 q = σ ( x ) q=\sigma(x) q=σ(x),则 σ ( x a ) = σ ( P q a ) \sigma(xa)=\sigma(P_{q}a) σ(xa)=σ(Pqa)

也就是说, P q P_{q} Pq这个后缀已经到了左边界,即从左边界开始求即可。

定理:如果 ϕ \phi ϕ是有限自动机的终态函数,那么 ϕ ( T i ) = σ ( T i ) \phi(T_{i}) = \sigma(T_{i}) ϕ(Ti)=σ(Ti)

证明对 i i i进行归纳即可。

计算状态转移函数

先确定一个 k k k的上界,然后在缩小范围即可,这种方式效率很低,接下来的 K M P KMP KMP算法可以优化这个思路。

代码

int sp[100][10];int eval(const string & p)
{for(int q = 0; q<=p.size(); q++){for(char a = '0'; a<='9'; a++){int k = q + 1;string x = p.substr(0,q) + a;while(1){bool ok = true;for(int i = x.size() - 1,j=k-1; j>=0; i--,j--){if(p[j] != x[i]){ok = false;break;}}if(ok) break;else k--;}sp[q][a] = k;}}
}void match(const string & t,int end)
{int q = 0;for(int i = 0; i<t.size(); i++){q = sp[q][t[i]];if(q == end) cout << i - end + 1 << endl;}
}int main()
{string p = "01011";string t = "0101111010111010101011111";int end = p.size();eval(p);match(t,end);return 0;
}

五、Knuth-Morris-Pratt算法

现在来介绍一种线性的字符串匹配算法 K M P KMP KMP算法。和自动机算法相比,他只需要用到一个辅助函数 π \pi π,而且这个函数和字符集无关,不需要遍历字符集。并且在 O ( m ) O(m) O(m)的时间计算出所有的函数值以留备用,在 O ( n ) O(n) O(n)的时间内完成匹配。

模式的前缀函数

匹配

面对一个偏移 s s s,匹配长度为 q q q,字符 q + 1 q+1 q+1匹配失败,按照朴素算法,我们应该测试下一个偏移 s + 1 s+1 s+1,但是我们知道,偏移 s + 1 s+1 s+1必然失败,因为 P [ 1 ] P[1] P[1]必然不可能匹配 T [ s + 2 ] T[s+2] T[s+2],可以匹配的最短偏移为 s + 2 s+2 s+2。这个 2 2 2我们可以通过计算前缀函数计算出来。

也就是说,假设模式字符串 P [ 1 … q ] P[1 \ldots q] P[1q]与文本字符串 T [ s + 1 … s + q ] T[s+1 \ldots s+q] T[s+1s+q]匹配, s ′ s' s是最小的偏移量, s ′ > s s' \gt s s>s,那么对某些 k < q k \lt q k<q满足:

P [ 1 … k ] = T [ s ′ + 1 … s ′ + k ] P[1 \ldots k] = T[s'+1 \ldots s'+k] P[1k]=T[s+1s+k]

的最小偏移 s ′ s' s是多少,其中 s ′ + k = s + q s' + k=s+q s+k=s+q

这时,我们就需要借助前缀函数 π ( q ) \pi(q) π(q),其值为存在最长的子串,既是 P P P的前缀,又是 P q P_{q} Pq真后缀

已知一个模式 P [ 1 … m ] P[1 \ldots m] P[1m],模式 P P P的前缀函数是函数 π : { 1 , 2 , 3 , 4 , … , m } → { 0 , 1 , … , m − 1 } \pi: \{1,2,3,4,\ldots,m\} \to \{0,1,\ldots,m-1\} π:{1,2,3,4,,m}{0,1,,m1}的一个函数,满足:

π [ q ] = m a x ( k : k < q ∣ P k ⊐ P q ) \pi[q]=max(k: k < q | P_{k} \sqsupset P_{q}) π[q]=max(k:k<qPkPq)

之后我们就可以利用辅助函数匹配文本。

代码

#include <bits/stdc++.h>using namespace std;#define FR freopen("in.txt", "r", stdin)typedef long long ll;int nxt[1000005];void buildNext(string &p)
{nxt[0] = 0;for (int i = 1; i < p.size(); i++){nxt[i] = nxt[i - 1];while (p[nxt[i]] != p[i] && nxt[i] > 0){nxt[i] = nxt[nxt[i] - 1];}nxt[i] = p[nxt[i]] == p[i] ? nxt[i] + 1 : 0;}
}void kmp(string &t, string &p)
{int ti = 0;int pi = 0;while (ti < t.size()){if (t[ti] == p[pi]){if (pi == p.size() - 1){cout << ti + 2 - p.size() << endl;pi = nxt[pi];}else{pi++;}ti++;}else if (pi == 0){ti++;}else{pi = nxt[pi - 1];}}
}int main()
{string s1, s2;cin >> s1 >> s2;buildNext(s2);kmp(s1, s2);for (int i = 0; i < s2.size(); i++){cout << nxt[i] << " ";}return 0;
}

六、Z-函数(扩展KMP)

定义 Z Z Z数组 Z [ i ] Z[i] Z[i]为,字符串 s [ 0 … i − 1 ] s[0 \ldots i - 1] s[0i1]和字符串 s [ i … n − 1 ] s[i \ldots n-1] s[in1]的最长公共前缀长度。特别的 Z [ 0 ] = 0 Z[0] = 0 Z[0]=0

我们有线性时间 O ( n ) O(n) O(n)的算法来计算Z-函数,叫做Z-算法。


http://chatgpt.dhexx.cn/article/jjkRO5TB.shtml

相关文章

朴素字符串匹配

描述 字符串匹配问题的形式定义&#xff1a; 文本(Text)是一个长度为 n 的字符串:T;模式(Pattern)是一个长度为 m 且 m≤n 的字符串:P; T 和 P 中的元素都属于有限的字母表 Σ 表; 有效位移 (Valid Shift): 如果 0≤ s ≤n-m&#xff0c;并且 T[s1…sm] P[1…m]&#xff0c…

算法之字符串匹配一

目录 前言&#xff1a; BF算法&#xff1a; RK算法 总结&#xff1a; 参考资料 前言&#xff1a; 字符串匹配指的是一个短点的字符串与一个长点的字符串进行匹配&#xff0c;并确认短的字符串是否在长的字符串中存在。匹配算法有很多&#xff0c;本文介绍两种简单、容易…

字符串匹配算法(C语言实现)

目录 文章目录 前言 一、BF算法 二、KMP算法 1.算法介绍 2.算法思路 3.整体代码实现 总结 前言 字符串匹配算法又称模式匹配算法&#xff0c;该算法的目的是为了子串从主串中寻找是否有与其匹配的部分&#xff0c; 其可分为BF暴力检索、RK哈希检索、KMP、BM等等&#xff0c;本…

shell字符串匹配

一、简介 Bash Shell提供了很多字符串和文件处理的命令。如awk、expr、grep、sed等命令&#xff0c;还有文件的排序、合并和分割等一系列的操作命令。grep、sed和awk内容比较多故单独列出&#xff0c;本文只涉及字符串的处理和部分文本处理命令。 二、字符串处理 1、expr命令…

golang字符串匹配算法

简介 字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串&#xff08;称为模式串&#xff09;。在 Golang 中&#xff0c;可以使用最常见的字符串匹配算法之一&#xff1a;Knuth-Morris-Pratt&#xff08;KMP&#xff09;算法&#xff0c;它的时间复杂度为 O(nm…

【数据结构】字符串匹配(暴力匹配)

原理解析&#xff1a; 字符串匹配方法&#xff0c;创建两个字符串结构&#xff0c;主 串和子串比较。 主串字符 a 和 子串字符 c 不匹配&#xff0c;主串的指针向下移动&#xff0c;移动到上一次开始比较的下一个位置。 子串指向开始的位置。 主串字符 b 和 子串字符 c 不匹配…

字符串匹配算法比较

字符串匹配&#xff08;string match)是在实际工程中经常会碰到的问题&#xff0c;通常其输入是原字符串(String)和子串&#xff08;又称模式&#xff0c;Pattern)组成&#xff0c;输出为子串在原字符串中的首次出现的位置。通常精确的字符串搜索算法包括暴力搜索(Brute force)…

子串查找(字符串匹配)

子串查询 首先&#xff0c;我们来定义两个概念&#xff0c;主串和模式串。我们在字符串 A 中查找字符串 B&#xff0c;则 A 就是主串&#xff0c;B 就是模式串。我们把主串的长度记为 n&#xff0c;模式串长度记为 m。由于是在主串中查找模式串&#xff0c;因此&#xff0c;主串…

字符串匹配算法综述

字符串匹配算法综述 字符串匹配算法综述&#xff1a;BF、RK、KMP、BM、Sunday 字符串匹配算法&#xff0c;是在实际工程中经常遇到的问题&#xff0c;也是各大公司笔试面试的常考题目。此算法通常输入为原字符串&#xff08;string&#xff09;和子串&#xff08;pattern&…

字符串匹配算法详解

希望看到文章的你们&#xff0c;能够在今年的研究生考试中超常发挥。 愿你们都能考上自己心仪的学校&#xff0c;为你们的备考生涯划上一个完美的句号。做为你们的师兄有几句话想对你们说&#xff0c;希望这些话能对你们有一些帮助。 马上就要考试了&#xff0c;不要再继续啃难…

字符串匹配算法

字符串匹配就是在主串A中查找模式串B&#xff0c;例如在主串abababc中查找模式串abc是否存在&#xff0c;记主串A的长度为n&#xff0c;模式串B的长度为m&#xff0c;n>m。 BF算法 BF(Brute Force)算法&#xff0c;又叫暴力匹配算法或者朴素匹配算法&#xff0c;思路很简单…

字符串(字符串匹配)

一、字符串匹配问题、基础 1、假设文本是一个长度为n的数组T&#xff0c;而模式是长度为m的数组P&#xff0c;我们希望在文本T中寻找模式P 如果P出现在T中的第s个位置&#xff0c;那么我们称其有效偏移为s&#xff0c;在其他不匹配的位置称为无效偏移 2、如果字符串w是字符串…

字符串匹配

字符串匹配 1.朴素的串匹配算法(暴力解法) 1.1 分析 设t是目标串&#xff08;母串&#xff09;&#xff0c;p是模式串&#xff08;待匹配串&#xff09;&#xff0c;i , j 分别指向 模式串 和 目标串&#xff0c;m、n分别是模式串p和目标串t的长度。 从目标串的第0个字符&am…

Photoshop怎么给图片添加简介信息或者版权信息

转自&#xff1a;Photoshop怎么给摄影图片添加作者名字版权等信息? 有时我们点开一张图片的详细信息中可能可以看到各种属性信息&#xff0c;比如作者&#xff0c;时间&#xff0c;关键字&#xff0c;图片信息描述等属性&#xff0c;但是我们自己的拍摄的或者从别的地方获取的…

2022年中国版权保护中心计算机软件著作权登记最全申请步骤流程

一、前言二、实名认证1. 用户注册2. 实名认证 三、办理步骤1. 办理流程2. 填写申请表3. 提交申请文件4. 登记机构受理申请5. 审查6. 获得登记证书 四、登记申请所需文件1. 软件著作权登记申请表2. 软件&#xff08;程序、文档&#xff09;的鉴别材料3. 有关证明文件 五、申请表…

IDEA设置版权信息

File→Settings或者CtrlS快捷键。 Editor下面有个Copyright→Copyright Profiles 点击加号&#xff0c;然后输入名称。 然后修改成自己的信息&#xff1a; 其中第一个年份是本文件新建日期&#xff0c;后面的是最后一次修改年份。 中文版本&#xff1a; 版权所有(c) Jack魏 …

版权和版本信息

版权和版本信息的主要内容有&#xff1a; &#xff08;1&#xff09;版权信息&#xff1b; &#xff08;2&#xff09;文件名称、简要描述、创建日期和作者&#xff1b; &#xff08;3&#xff09;当前版本信息和说明&#xff1b; &#xff08;4&#xff09;历史版本信息和…

版权和商标权有什么关系?版权和商标的区别在哪里?

版权和商标权存在着一定的关系&#xff0c;版权和商标又有着很多区别&#xff0c;具体的关系和区别是怎样的&#xff0c;大家都知道吗&#xff1f;今天企多多就带大家来了解&#xff01; 版权和商标权的关系 版权和商标权的关系主要有以下三点&#xff1a; 1、关联性&#xf…

版权 | 收藏!哪些作品可以登记版权?

创业创新中&#xff0c;无论是公司LOGO还是IP形象或者产品手册&#xff0c;都凝结着无数的心血。当下互联网和科技的发展&#xff0c;让抄袭变得前所未有的容易&#xff0c;尤其在美术作品、文字作品和影视作品领域。如何有效地保护自己的智力成果呢&#xff1f;先从了解这些开…

Pyinstaller加入版本和版权信息

目录 参考链接 前言 一. 获取版本信息 1. 拖过来个有版本和版权信息的exe文件 2. 放置一个txt文件 我们接着放置一个txt文件叫file_version_info.txt。这名字不能改&#xff0c;Pyinstaller自动就把版权信息放在这里。 3.开始获取 二. 修改 三. 打包 参考链接 pyinsta…