首页 新闻 会员 周边 捐助

kmp算法的重复子串个数与不重复字串个数的两个代码的过程和不同之处是什么?

0
[待解决问题]

get_lps和get_next这两个主要在代码中的区别是什么?
在代码的实现中为何改变了是否为重复与不重复?
两个代码的实现过程是怎么样的?

const int N = 1e6 + 1;
int lps[N];
int nextarray[N];

void get_lps(string pat, int * lps) {
int m = pat.length();
int j = 0, i = 1;
lps[0] = 0;
while (i < m) {
if (pat[j] == pat[i]) {
j++;
lps[i] = j;
i++;
} else if (j != 0) {
j = lps[j - 1];
} else {
lps[i] = 0;
i++;
}
}
}

void get_next(string pat,int* lps)
{
int m = pat.length();
get_lps(pat,lps);
int i;
nextarray[0] = -1;

for(i = m - 2; i >= 0 ; i--){
lps[i+1] = lps[i];}
lps[0]= -1;
for(i=0;i<m;i++)
{
printf("%d",lps[i]);
if(i<m-1)
    printf(" ");
}

}
不重复的代码
void KMP(string txt, string pat)
{
//请在下面编写代码
/****Begin/
int n = txt.length();
int m = pat.length();
int i = 0;
int j = 0;
get_lps(pat, lps);
while (i < n && j < m) {
if (txt[i] == pat[j]) {
i++; j++;
} else if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
if (j == m) {
cout << i - j + 1 << endl;
j = lps[j - 1];
}
}
get_next(pat,lps);
}
重复的代码
int Count(string s,string t)
{
int n=s.length();
int m=t.length();
int i=0;
int j=0;
int tot=0;
get_next(t,nt);
while(i<n&&j<m)
{
if(jm-1&&s[i]t[j])
{
tot++;
j=nt[j];
}
if(j-1||s[i]t[j])
{
i++;
j++;
}
else j=nt[j];
}
return tot;
}

int main(int argc, char const *argv[])
{
string txt, pat;

cin >> txt >> pat;  //输入文本串和模式串

KMP(txt, pat);    //调用kmp算法输出模式串在文本串中的位置以及next[]数组的值

return 0;

}

小狗元元的主页 小狗元元 | 菜鸟二级 | 园豆:202
提问于:2024-11-16 12:10
< >
分享
所有回答(1)
0

KMP算法(Knut-Morris-Pratt算法)是用于字符串匹配的一种高效算法,旨在减少搜索过程中的不必要比较。您提到的重复子串个数和不重复子串个数的计算逻辑,虽然基于KMP算法的核心思想,但具体实现上有所不同。接下来,我将逐步分析这两个代码的过程及其不同之处,并重点阐述 get_lpsget_next 函数的实现。

1. KMP算法基本概念

在KMP算法中,“最长前缀数组”(LPS, Longest Prefix that is also Suffix)用于减少字符比较的次数。当模式串与文本串匹配失败时,可以利用LPS数组快速跳过一些已知的匹配数据。

2. get_lpsget_next的主要区别

  • get_lps 函数

    • 计算给定模式字符串 pat 的LPS数组。
    • LPS数组中的每个值表示模式串的前缀的最长相等的后缀的长度。
    • 此数组用于在匹配过程中,当发生不匹配时,可以根据LPS值跳过文本串中的一些位置。
  • get_next 函数

    • 在计算LPS数组后,可以构建一个“next”数组,通常用于处理更复杂或需要多个匹配的情况。
    • next数组通常将最后一个元素设为-1(表示没有相应的前缀),并且对LPS数组进行了偏移,使得其结构有所不同。
    • 该函数也可能对不同的重复子串的计数逻辑进行调整,使其更适用于计算重复或不重复的子串。

3. 不重复子串与重复子串的实现过程

不重复子串的 KMP 实现

void KMP(string txt, string pat) {
    int n = txt.length();
    int m = pat.length();
    int i = 0, j = 0;
    get_lps(pat, lps); // 获取LPS数组
    while (i < n && j < m) {
        if (txt[i] == pat[j]) {
            i++;
            j++;
        } else if (j != 0) {
            j = lps[j - 1];
        } else {
            i++;
        }
        if (j == m) {  // 找到匹配
            cout << i - j + 1 << endl; // 输出匹配位置
            j = lps[j - 1]; // 继续查找下一个匹配
        }
    }
}

过程解析

  • 循环遍历文本 txt 和模式串 pat
  • 每次遇到匹配时,记录位置并尝试继续查找后续匹配。
  • 使用LPS数组进行跳转,以避免重复比较已匹配的字符。

重复子串的 KMP 实现

int Count(string s, string t) {
    int n = s.length();
    int m = t.length();
    int i = 0, j = 0;
    int tot = 0;
    get_next(t, nt); // 先计算next数组并返回

    while (i < n && j < m) {
        if (j == m - 1 && s[i] == t[j]) { // 完全匹配
            tot++;  // 计数这个匹配
            j = nt[j];  // 使用next数组跳过某些匹配
        }
        if (j == m - 1 || s[i] == t[j]) {
            i++;
            j++;
        } else {
            j = nt[j]; // 使用next数组回退
        }
    }
    return tot;  // 返回成功匹配的总数
}

过程解析

  • 与不重复子串的实现类似,但是这里统计每个完整的模式串匹配,仅在匹配成功时增加计数tot
  • 通过next数组的跳转逻辑更改匹配的处理,确保遍历所有可能的重复匹配,不限制于首次匹配后的行为。

4. 为何改变了是否为重复与不重复?

  • 重复子串计数要求统一处理所有可能的匹配,无论其位置是否相同,适合场景如文本分析统计。
  • 不重复子串的查找则更关注模式位置的唯一性。通常返回的是找到的匹配的首次位置,更符合查找模式的实际需求。

总体上,这两者的不同在于处理成功匹配的逻辑以及跳转的方式,以适应不同的数据计数需求。在具体实现过程中,LPS和next数组的计算、利用方式的不同直接影响到算法的执行效果和功能目的。

Technologyforgood | 园豆:7700 (大侠五级) | 2024-11-28 09:10
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册