ACM常用模板——扩展KMP

模板:扩展KMP,用extend[i]保存 主串 S[i.....n-1]与 模式串 T的最长公共前缀的长度
   
   
  1. using namespace std;
  2. int ne[maxn],extend[maxn];
  3. void EKMP(char s[],char t[])//s[]为主串,t[]为模版串
  4. {
  5. int i,j,p,l;
  6. int len=strlen(t);
  7. int len1=strlen(s);
  8. memset(ne,0,sizeof(ne));
  9. memset(extend,0,sizeof(extend));
  10. ne[0]=len;
  11. j=0;
  12. while(1+j<len&&t[j]==t[1+j])j++;
  13. ne[1]=j;
  14. int a=1;
  15. for(i=2; i<len; i++)
  16. {
  17. p=ne[a]+a-1;
  18. l=ne[i-a];
  19. if(i+l<p+1)ne[i]=l;
  20. else
  21. {
  22. j=max(0,p-i+1);
  23. while(i+j<len&&t[i+j]==t[0+j])j++;
  24. ne[i]=j;
  25. a=i;
  26. }
  27. }
  28. j=0;
  29. while(j<len1&&j<len&&s[j]==t[j])j++;
  30. extend[0]=j;
  31. a=0;
  32. for(i=1; i<len1; i++)
  33. {
  34. p=extend[a]+a-1;
  35. l=ne[i-a];
  36. if(l+i<p+1)extend[i]=ne[i-a];
  37. else
  38. {
  39. j=max(0,p-i+1);
  40. while(i+j<len1&&j<len&&s[i+j]==t[j])j++;
  41. extend[i]=j;
  42. a=i;
  43. }
  44. }
  45. }
  46. int main().
  47. {
  48. char a[maxn],b[maxn];
  49. while(~scanf("%s%s",a,b)){
  50. EKMP(a,b);
  51. for(int i=0;i<strlen(a);i++)cout<<extend[i]<<" ";
  52. }
  53. return 0;
  54. }
    
    
  1. const int maxn=100010; //字符串长度最大值
  2. int next[maxn],ex[maxn]; //ex数组即为extend数组
  3. //预处理计算next数组
  4. void GETNEXT(char *str)
  5. {
  6. int i=0,j,po,len=strlen(str);
  7. next[0]=len;//初始化next[0]
  8. while(str[i]==str[i+1]&&i+1<len)//计算next[1]
  9. i++;
  10. next[1]=i;
  11. po=1;//初始化po的位置
  12. for(i=2;i<len;i++)
  13. {
  14. if(next[i-po]+i<next[po]+po)//第一种情况,可以直接得到next[i]的值
  15. next[i]=next[i-po];
  16. else//第二种情况,要继续匹配才能得到next[i]的值
  17. {
  18. j=next[po]+po-i;
  19. if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
  20. while(i+j<len&&str[j]==str[j+i])//计算next[i]
  21. j++;
  22. next[i]=j;
  23. po=i;//更新po的位置
  24. }
  25. }
  26. }
  27. //计算extend数组
  28. void EXKMP(char *s1,char *s2)
  29. {
  30. int i=0,j,po,len=strlen(s1),l2=strlen(s2);
  31. GETNEXT(s2);//计算子串的next数组
  32. while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0]
  33. i++;
  34. ex[0]=i;
  35. po=0;//初始化po的位置
  36. for(i=1;i<len;i++)
  37. {
  38. if(next[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
  39. ex[i]=next[i-po];
  40. else//第二种情况,要继续匹配才能得到ex[i]的值
  41. {
  42. j=ex[po]+po-i;
  43. if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
  44. while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
  45. j++;
  46. ex[i]=j;
  47. po=i;//更新po的位置
  48. }
  49. }
  50. }
hdu4333

给一个数字字符串S,  可以把S最后一个数字移动到最前面变成另一个数字。例如123,  经过移动依次变成312,231,123。 注意当移动次数正好和S长度相等时,S又变回了最开始的那个数字。

求这个移动过程所形成的所有字符串,大于S(最初的)的数字,等于S,以及小于S的各有多少个。


扩展KMP能求出一个串所有后缀串(即s[i...len])和模式串的最长公共前缀。于是只要将这个串复制一遍,求出该串每个后缀与其本身的最长公共前缀即可,当公共前缀>=len时,显然相等,否则只要比较下一位就能确定这个串与原串的大小关系。

  至于重复串的问题,只有当这个串有循环节的时候才会产生重复串,用KMP的next数组求出最小循环节,用长度除以最小循环节得到循环节个数,在将3个答案都除以循环节个数即可。

      
      
  1. #include<cstdio>
  2. #include <iostream>
  3. #include <cmath>
  4. #include <cstring>
  5. #include <algorithm>
  6. #include <set>
  7. #include <map>
  8. #include <queue>
  9. #include <vector>
  10. #define maxn 100005
  11. #define LL long long
  12. #define MS(a,b) memset(a,b,sizeof(a))
  13. #define FI(a,b) fill(a,a+maxn,b)
  14. #define sf(n) scanf("%d",&n)
  15. #define sf2(a,b) scanf("%d%d",&a,&b)
  16. #define pf(n) printf("%d\n",n)
  17. #define ffr(i,n) for(i=0;i<n;i++)
  18. /*
  19. s:abcdabcadcabcdcab
  20. p: abcdcab
  21. next:-1000001
  22. */
  23. using namespace std;
  24. int ne[maxn],extend[maxn],m,l,r,knext[maxn];
  25. void EKMP(char s[],char t[])//s[]为主串,t[]为模版串
  26. {
  27. int i,j,p,l;
  28. int len=strlen(t);
  29. int len1=strlen(s);
  30. memset(ne,0,sizeof(ne));
  31. memset(extend,0,sizeof(extend));
  32. ne[0]=len;
  33. j=0;
  34. while(1+j<len&&t[j]==t[1+j])j++;
  35. ne[1]=j;
  36. int a=1;
  37. for(i=2; i<len; i++)
  38. {
  39. p=ne[a]+a-1;
  40. l=ne[i-a];
  41. if(i+l<p+1)ne[i]=l;
  42. else
  43. {
  44. j=max(0,p-i+1);
  45. while(i+j<len&&t[i+j]==t[0+j])j++;
  46. ne[i]=j;
  47. a=i;
  48. }
  49. }
  50. j=0;
  51. while(j<len1&&j<len&&s[j]==t[j])j++;
  52. extend[0]=j;
  53. a=0;
  54. for(i=1; i<len1; i++)
  55. {
  56. p=extend[a]+a-1;
  57. l=ne[i-a];
  58. if(l+i<p+1)extend[i]=ne[i-a];
  59. else
  60. {
  61. j=max(0,p-i+1);
  62. while(i+j<len1&&j<len&&s[i+j]==t[j])j++;
  63. extend[i]=j;
  64. a=i;
  65. }
  66. }
  67. }
  68. void Getnxt(char *S){
  69. knext[0]=-1;
  70. int i=1,j=0;
  71. int len=strlen(S);
  72. while(i<len){
  73. if(S[i]==S[j]||j==-1){
  74. i++;
  75. j++;
  76. knext[i]=j;
  77. }else{
  78. j=knext[j];
  79. }
  80. }
  81. }
  82. int main()
  83. {
  84. char a[maxn*2],b[maxn];
  85. int Case=1,t,i,j;
  86. sf(t);
  87. while(t--){
  88. scanf("%s",a);
  89. strcpy(b,a);
  90. strcat(a,b);
  91. //cout<<a<<endl;
  92. EKMP(a,b);
  93. int len=strlen(b);
  94. m=0;r=0;l=0;
  95. for(i=0;i<len;i++){
  96. j=extend[i];
  97. if(j>=len)m++;
  98. else if(b[j]>a[i+j])l++;
  99. else if(b[j]<a[i+j])r++;
  100. //cout<<b[]
  101. }
  102. Getnxt(b);
  103. int qt=1;
  104. int t=len-knext[len];
  105. if(len%t==0){
  106. qt=len/t;//求出循环节;
  107. }
  108. printf("Case %d: %d %d %d\n",Case++,l/qt,m/qt,r/qt);
  109. }
  110. return 0;
  111. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值