kmp2-HDU1358 HUST1010 POJ2406 POJ2752

举报
兔老大 发表于 2021/04/22 00:54:54 2021/04/22
【摘要】 HDU1358 http://acm.hdu.edu.cn/showproblem.php?pid=1358   先构造出 next[] 数组,下标为 i,定义一个变量 j = i - next[i] 就是next数组下标和下标对应值的差,如果这个差能整除下标 i,即 i%j==0 ,则说明下标i之前的字符串(周期性字符串长度为 i)一定可...

HDU1358

http://acm.hdu.edu.cn/showproblem.php?pid=1358

 

先构造出 next[] 数组,下标为 i,定义一个变量 j = i - next[i] 就是next数组下标和下标对应值的差,如果这个差能整除下标 i,即 i%j==0 ,则说明下标i之前的字符串(周期性字符串长度为 i)一定可以由一个前缀周期性的表示出来,这个前缀的长度为刚才求得的那个差,即 j,则这个前缀出现的次数为 i/j 


  
  1. #include <iostream>
  2. #include <stdio.h>
  3. using namespace std;
  4. char a[1000010];
  5. int next[1000010];
  6. int n;
  7. void GetNext() //获得a数列的next数组
  8. {
  9. int i=0,k=-1;
  10. next[0] = -1;
  11. while(i<n){
  12. if(k==-1){
  13. next[i+1] = 0;
  14. i++;k++;
  15. }
  16. else if(a[i]==a[k]){
  17. next[i+1] = k+1;
  18. i++;k++;
  19. }
  20. else
  21. k = next[k];
  22. }
  23. }
  24. void DisRes(int num)
  25. {
  26. int j;
  27. printf("Test case #%d\n",num);
  28. for(int i=0;i<=n;i++){
  29. if(next[i]==-1 || next[i]==0) //next[i]是-1或0的忽略,说明之前没有周期性前缀
  30. continue;
  31. j = i - next[i];
  32. if(i%j==0) //能整除,说明存在周期性前缀
  33. printf("%d %d\n",i,i/j); //输出这个前缀的长度和周期数
  34. }
  35. printf("\n");
  36. }
  37. int main()
  38. {
  39. int num = 0;
  40. while(scanf("%d",&n)!=EOF){
  41. if(n==0) break;
  42. scanf("%s",a);
  43. GetNext(); //获得next[]数组
  44. DisRes(++num); //输出结果
  45. }
  46. return 0;
  47. }

 

HUST 1010

http://www.hustoj.org/problem/1010

读错题了,B是减下来的那一堆,那就好做了。找最短循环节就可以了。(循环节:从某一位起向右进行到某一位止的一节数字循环出现,首尾衔接,称这种小数为循环小数,这一节数字称为循环节。)


  
  1. #include<stdio.h>
  2. #include<string.h>
  3. #define N 1000005
  4. char str[N];
  5. int f[N];
  6. void getFail(char *p, int *f)
  7. {
  8. int m=strlen(p);
  9. f[0]=f[1]=0;
  10. for(int i=1; i<m; ++i)
  11. {
  12. int j=f[i];
  13. while(j && p[i]!=p[j])j=f[j];
  14. f[i+1] = p[i]==p[j]?1+j:0;
  15. }
  16. }
  17. int main()
  18. {
  19. while(scanf("%s",str)!=EOF)
  20. {
  21. int len=strlen(str);
  22. getFail(str,f);
  23. printf("%d\n",len-f[len]);
  24. }
  25. return 0;
  26. }

 POJ 2406

http://poj.org/problem?id=2406

 

大意:给出一个字符串 问它最多由多少相同的字串组成 

如  abababab由4个ab组成

最小循环节

ababab  next[6] = 4; 即

ababab

   ababab

1~4位  与2~6位是相同的

 

那么前两位

就等于3、4位

3、4位就等于5、6位

……

所以 如果 能整除  也就循环到最后了

 

如果不能整除  

就最后余下的几位不在循环内

 


  
  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. using namespace std;
  5. const int maxn = 1000005;
  6. int next[maxn];
  7. void get(char *s) {
  8. int l = strlen(s);
  9. int j = 0, k = -1;
  10. next[0] = -1;
  11. while(j < l) {
  12. if(k == -1 || s[j] == s[k]) {
  13. next[++j] = ++k;
  14. } else {
  15. k = next[k];
  16. }
  17. }
  18. }
  19. char s[maxn];
  20. int main() {
  21. while(gets(s) ) {
  22. if(strcmp(s,".") == 0) {
  23. break;
  24. }
  25. get(s);
  26. int ans = 1;
  27. int l = strlen(s);
  28. if(l % (l - next[l]) == 0) {
  29. ans = l / (l - next[l]);
  30. }
  31. printf("%d\n", ans);
  32. }
  33. }

POJ2752

http://poj.org/problem?id=2752

大意:给定字符串S,求出S的所有可能相同前后缀的长度。比如: 
"alala"的前缀分别为{"a", "al", "ala", "alal", "alala"}, 后缀分别为{"a", "la", "ala", "lala", "alala"}. 其中有{"a", "ala", "alala"}是相同的,即输入1,3,5.

思路:有了kmp算法的next数组(保存某段子串的相同前后缀的最大长度,不包括自身),可以从最长的相同前后缀开始考虑,对于串S,根据next[strlen(S)-1]得到的最长相同前后缀为S1,则再根据next[strlen(S1)-1]得到的S1的最长相同前后缀S2肯定也为S的相同前后缀。(通过画图可以很容易的证明)。 
    这样,通过不断的向前查找next数组,得到一系列的相同前后缀。下面证明这些前后缀包含了所有的相同前后缀: 
        由于我们得到的是一系列最长相同前后缀,假设有一个相同的前后缀Sk不属于这些通过next递推得到的前后缀集合,那么len(Sk)必定小于len(S1),且Sk必定为S1的子串(通过画图很容易看出来);此时若Sk是S1的最长前后缀,则与前提Sk不为某一子串的最长前后缀矛盾,那么Sk不是S1的最长前后缀,则可以退出len(Sk)必定小于S2,且Sk必定为S2的相同前后缀..... 数学归纳递推下去,可以知道Sk要么为某个Si的最长前后缀,要么len(Sk) = 0. 这样可以证明这些通过next数组递推得到的前后缀包含了所有的相同前后缀


  
  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. using namespace std;
  5. int Next[400005];
  6. char str[400005];
  7. int ans[400005];
  8. int cnt;
  9. int len;
  10. void getNext()
  11. {
  12. Next[0] = -1;
  13. int i = 0, j = -1;
  14. while (i < len)
  15. {
  16. if (j == -1 || str[i] == str[j])
  17. {
  18. ++i;
  19. ++j;
  20. Next[i] = j;
  21. }
  22. else j = Next[j];
  23. }
  24. }
  25. int main()
  26. {
  27. while (scanf("%s", str) != EOF)
  28. {
  29. len = strlen(str);
  30. getNext();
  31. cnt = 0;
  32. int t = Next[len - 1];
  33. while (t != -1)
  34. {
  35. if (str[t] == str[len - 1]) ans[cnt++] = t + 1;
  36. t = Next[t];
  37. }
  38. for (int i = cnt - 1; i >= 0; --i)
  39. {
  40. printf("%d ", ans[i]);
  41. }
  42. printf("%d\n", len);
  43. }
  44. return 0;
  45. }

 

文章来源: fantianzuo.blog.csdn.net,作者:兔老大RabbitMQ,版权归原作者所有,如需转载,请联系作者。

原文链接:fantianzuo.blog.csdn.net/article/details/82492803

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。