字符串基础


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),j=0;nxt[0]=0;
    for(int i=1;i<len;++i){
        while(j>0&&s[i]!=s[j])j=nxt[j];
        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 有 π[i]=n 成立,则字符串 s 在字符串 t 的 i-n+1-(n+1)=i-2n 处出现。

上代码

#include<bits/stdc++.h>
using namespace std;
string s,t,a;int nxt[1010100],n,ans,le;
void get_nxt(const string s){
    int len=s.size(),j=0;nxt[0]=0;
    for(int i=1;i<len;++i){
        while(j>0&&s[i]!=s[j])j=nxt[j-1];
        if(s[i]==s[j])  ++j;
        nxt[i]=j;
        if(j==le)  ans++;        
        //cout<<j<<endl;
    }
}
int main(){
    cin>>n;
    while(n--){
        memset(nxt,0,sizeof(nxt));
        cin>>s>>t;ans=0;
        a=s+'#'+t;le=s.size();//cout<<le<<endl;
        get_nxt(a);cout<<ans<<endl;
    }
}

 

原创文章,作者:kirin,如若转载,请注明出处:https://blog.ytso.com/276410.html

(0)
上一篇 2022年7月23日
下一篇 2022年7月23日

相关推荐

发表回复

登录后才能评论