Zoj 3587 Marlon's String (KMP 字符串拼接 前缀出现次数)

红太狼 2022-08-23 12:58 69阅读 0赞

题意:给字符串S,T,找到所有的tetrad (a,b,c,d), Sa..b + Sc..d = T , a≤b and c≤d.也就是把T分成两段,这两段都由S中的子串组成的,求有多少中组合方式(S中的两个子串可重叠)

这题纠结了很长时间,看了这个之后才发现自己的思路漏了一种情况(代码中注释的部分):【ZOJ3587】Marlon’s String

网上还有人用exKMP做的 zoj 3587 Marlon’s String(拓展KMP+dp)

关于代码中注释部分的解释:用cnt[i]记录一个前缀的出现次数,最后统计cnt[next[i]]+=cnt[i]。因为如果前缀j能在i这个位置出现一次那么next[j]一定能在i这个位置出现一次。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N=100005;
  6. char ss[N],tt[N];
  7. int next[N],cnt1[N],cnt2[N];
  8. int len1,len2;
  9. void getNext (char s[],int len)
  10. {
  11. next[0]=-1;
  12. int i=0,j=-1;
  13. while (i<len)
  14. {
  15. if (j==-1 || s[i]==s[j])
  16. next[++i]=++j;
  17. else j=next[j];
  18. }
  19. }
  20. void KMP (int *cnt)
  21. {
  22. int i=0,j=0;
  23. while (i<len1)
  24. if (j == -1 || ss[i] == tt[j])
  25. i++,j++,cnt[j]++;
  26. else
  27. j=next[j];
  28. for (i=len2;i>=0;i--) //很重要,注意理解
  29. if (next[i] != -1)
  30. cnt[next[i]] += cnt[i];
  31. }
  32. void reverse (char *str)
  33. {
  34. int len = strlen(str);
  35. for (int i=0;i<len/2;i++)
  36. swap (str[i],str[len-i-1]);
  37. }
  38. int main ()
  39. {
  40. int T;
  41. scanf("%d",&T);
  42. while (T--)
  43. {
  44. scanf("%s%s",ss,tt);
  45. len1 = strlen(ss);
  46. len2 = strlen(tt);
  47. memset(cnt1,0,sizeof(cnt1));
  48. memset(cnt2,0,sizeof(cnt2));
  49. getNext (tt,len2);
  50. KMP(cnt1);
  51. reverse(ss);
  52. reverse(tt);
  53. getNext(tt,len2);
  54. KMP(cnt2);
  55. long long ans = 0;
  56. for (int i=1;i<len2;i++)
  57. ans += (long long)cnt1[i] * cnt2[len2-i];
  58. printf("%lld\n",ans);
  59. }
  60. return 0;
  61. }

发表评论

表情:
评论列表 (有 0 条评论,69人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Zoj 3587 Marlons String

    题意: 给定两个串S和T,从S中找两个子串组成T(拼起来和T一模一样),两个子串可重叠,问有几种组合方法? 三种方法: KMP+计数 直接用T去匹配S,匹配到T中