• KMP算法 → 计算nextval数组


    【算法解析】
    ● 众所周知,KMP算法中模式串T的next数组,是KMP算法的核心。
    next数组的核心作用是“
    当模式串T的第j位与主串S的第pos位失配时(即 T[j]≠S[pos] 时),让模式串T的第next[j]位与主串S的第pos位再进行比较”。这相当于让模式串T往右移动了 j-next[j] 位后,再进行比较。

    ● 正是由于next数组的引入,使得KMP算法的效率大大优于BF算法。但是,next数组在某些情况下仍存在缺陷。
    例如,针对模式串"aaaab",其相应的next数组值为“-1 0 1 2 3”。则据next数组的作用易知,当 T[3]=a 与主串 S[pos] 失配时,则会使用 T[next[3]]=T[2]=a 与主串 S[pos] 再进行匹配,显然还会失配。这是因为,T[0]~T[2]与T[3]都相等,当T[3]与S[pos]失配时,T[0]~T[2]与S[pos]必然也失配。显然T[0]~T[2]与S[pos]的比较
    没有意义,一定会匹配失败。针对本模式串"aaaab"而言,将会有 T[3]=a≠S[pos],T[next[3]]=T[2]=a≠S[pos],T[next[2]]=T[1]=a≠S[pos],T[next[1]]=T[0]=a≠S[pos] 等四次比较。

    ● nextval数组的引入,正是为了减少这种无意义的比对,而对next数组进行的优化

     


    【手动求nextval数组的步骤】
    ● 手动求nextval数组(模式串下标从1开始)
    若在KMP算法设计中,将模式串下标从1开始计数,那么求nextval数组的算法步骤为:
    (1)求出next数组的值(定义next[1]=0,next[2]=1);
    (2)定义nextval[1]=0。然后,比较模式串的第j个
    (j>1)字符是否与第next[j]个字符相等。若相等,则模式串第j个字符的nextval值等于第next[j]个字符的nextval值。若不等,则模式串第j个字符的nextval值等于其next数组值。
    即,若T[j]=T[next[j]],则nextval[j]=nextval[next[j]]。否则,nextval[j]=next[j]。

    ● 手动求nextval数组(模式串下标从0开始)
    若在KMP算法设计中,将模式串下标从0开始计数,那么求nextval数组的算法步骤为:
    (1)求出next数组的值(定义next[0]=-1,next[1]=0);
    (2)定义nextval[0]=-1。然后,比较模式串的第j个
    (j>0)字符是否与第next[j]个字符相等。若相等,则模式串第j个字符的nextval值等于第next[j]个字符的nextval值。若不等,则模式串第j个字符的nextval值等于其next数组值。
    即,若T[j]=T[next[j]],则nextval[j]=nextval[next[j]]。否则,nextval[j]=next[j]。

    【求nextval数组的算法代码】

    1. #include
    2. using namespace std;
    3. const int maxn=100;
    4. int ne[maxn],nev[maxn];
    5. void getNext(string s) {
    6. int len=s.length();
    7. int i=0,j=-1;
    8. ne[0]=-1;
    9. while(i
    10. if(j==-1||s[i]==s[j]) {
    11. i++;
    12. j++;
    13. ne[i]=j;
    14. } else j=ne[j];
    15. }
    16. }
    17. void getNextval(string s) {
    18. int len=s.length();
    19. int i=0,j=-1;
    20. nev[0]=-1;
    21. while(i
    22. if(j==-1||s[i]==s[j]) {
    23. i++;
    24. j++;
    25. nev[i]=j;
    26. if(s[i]!=s[j]) nev[i]=j;
    27. else nev[i]=nev[j];
    28. } else j=nev[j];
    29. }
    30. }
    31. int main() {
    32. string T;
    33. getline(cin,T);
    34. getNext(T);
    35. for(int i=0; ilength(); i++) {
    36. cout<" ";
    37. }
    38. cout<
    39. getNextval(T);
    40. for(int i=0; ilength(); i++) {
    41. cout<" ";
    42. }
    43. return 0;
    44. }
    45. /*
    46. input:
    47. abcaabbabcab
    48. output:
    49. -1 0 0 0 1 1 2 0 1 2 3 4
    50. -1 0 0 -1 1 0 2 -1 0 0 -1 4
    51. */


    【编程技巧】
    ● 由于字符串的下标从0开始,因此采用李春葆《数据结构》中的方式定义nextval[0]=-1,nextval[1]=0是很自然的事情。之后,利用语句 cout< ● 若想输出以“0 1”开头的nextval数组值,只需修改语句cout<cout< 便可。其他代码保持不变。


    【参考文献】
    https://blog.csdn.net/qq_43456605/article/details/119954614
    https://blog.csdn.net/qq_35963993/article/details/106236665



     

  • 相关阅读:
    pytorch 学习第三天 交叉熵
    Jquery 老项目引入vue,elementui
    Linux:线程池
    互联网历史
    软件工程毕业设计课题(80)微信小程序毕业设计PHP电影视频播放小程序系统设计与实现
    【SpringBoot】必须掌握的45个注解
    CV相关 - 文献地址地址、数据集地址
    linux下常用命令
    怎么把加密的PDF解密?安利几个办公小技巧
    贴纸拼词 —— 记忆化搜索 / 状压DP
  • 原文地址:https://blog.csdn.net/hnjzsyjyj/article/details/127105603