KMP

应用:求字符串s在文本T中出现的次数与位置

概念:

后缀  从某个位置 i 开始到整个串末尾结束的一个特殊子串。字符串 S 的从 i 开头的后缀表示为 Suffix(S, i),也就是 Suffix(S, i) = S[i…|S|-1]。

真后缀  除了 S 本身的 S 的后缀

前缀  从串首开始到某个位置 i 结束的一个特殊子串。字符串 S 的以 i 结尾的前缀表示为 Prefix(S, i),也就是 Prefix(S, i) = S[0…i]。

真前缀  除了 S 本身的 S 的前缀

Border  如果一个串s’同时是 S 的真前缀和真后缀,那么称s’是S的一个border

原理:nxt【i】为s【0-i】最长相等前缀和与后缀和.对于nxt【i+1】,最大为nxt【i】+1,此时s【i+1】==s【nxt【i】+1】。那么否则呢,如何找到i+1前一部分与s【】开头一部分最大重合?

现在来看这一组字符串

 

 观察i的前面nxt【i-1】=5,可知A=B,同位代换,D=E.而在nxt【A】=2,即C=D,所以C=E.这就找到了最大重合,又发现s【nxt【A】+1】==s【i】,所以nxt【i】=nxt【A】+1,而A=nxt【i-1】。

就这样,整个函数最难懂的地方就出来了while(j>0&&s[i]!=s[j])j=nxt[j];

上代码

void get_nxt(const char s[]){
    int len=strlen(s);for(int i=1,j=0;i<len;++i){
        while(j>0&&s[i]!=s[j])  j=nxt[j-1];
        if(s[i]==s[j])  ++j;
        nxt[i]=j;        
    }
}

nxt已准备就绪,现在解决T中s的个数

大多数人编了两个函数,但是duck不必,我们灵活运用get_nxt()

为了简便起见,我们用 n 表示字符串 s 的长度,用 m 表示文本 t 的长度。
我们构造一个字符串 s+#+t,其中 # 为一个既不出现在 s 中也不出现在 t 中的分隔符。接下来计算该字符串的前缀函数。现在考虑该前缀函数除去最开始n+1 个值(即属于字符串 s 和分隔符的函数值)后其余函数值的意义。
根据定义, nxt[i] 为右端点在 i 且同时为一个前缀的最长真子串的长度,具体到我们的这种情况下,其值为与 s 的前缀相同且右端点位于 i 的最长子串的长度。
由于分隔符的存在,该长度不可能超过 n。而如果等式 nxt[i]=n 成立,则意味着 s 完整出现在该位置(即其右端点位于位置 i)。注意该位置的下标是对字符串 s+#+t 而言的。
因此如果在某一位置 i 有 nxt[i]=n 成立,则字符串 s 在字符串 t 的 i-n+1-(n+1)=i-2n 处出现。

 最后是用函数单独求个数,出现位置,适用于在文本T中查找多个s

上代码

int coun(char s[],char t[]){
    int len=strlen(s),lem=strlen(t),ans=0;
    for(int i=0,j=0;i<lem;++i){
        while(j>0&&t[i]!=s[j])  j=nxt[j-1];
        if(t[i]==s[j])  ++j;
        if(j==n)  num[++ans]=i-n+2,j=nxt[j];
    }return ans;

 特别注意

当数组以下标0开始时,j=nxt[j-1];以1开始时,j=nxt[j]

posted @ 2022-07-23 11:46  yisiwunian  阅读(26)  评论(0编辑  收藏  举报