Codeforces 631D Messenger(Z-Box or kmp)

你的名字 2021-10-18 09:34 290阅读 0赞

题意:给出两个分别为n,m项的字符串,求第二个字符串在第一个中出现几次,字符串按照(li,ci)的形式给出。(如2-a 2-b 1-c 表示aabbc),n,m<=2e5 l<=1e6。

分析:字符串总长度过长,直接匹配比较困难,可以把一个字符串(li,ci)看成一个字母。容易发现,去掉头尾两个二元组的话,中间那些部分必须完全相等才能匹配。采用如下方式构造新串:将文本串(大串)接在去掉头尾两个二元组的模式串上,获取它的z数组。 然后我们就可以先找到能够匹配中间部分的位置,此时我们再单独比较头尾是否可行即可。 这种方法需要特判1,因为去掉头尾是无法看出长度为1的串的。参考https://blog.csdn.net/szh_0808/article/details/79257961。

代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. const int MAXN=1e6;
  5. int n,m,z[2*MAXN];
  6. ll t1[MAXN],t2[MAXN],t[2*MAXN],ans;
  7. char s1[MAXN],s2[MAXN],s[2*MAXN];
  8. void get_z() {
  9. int N=n+m-1;
  10. int l=0,r=0;
  11. for (int i=1;i<N;i++) {
  12. if (i>r) {
  13. l=i,r=i;
  14. while (r<N && t[r]==t[r-l] && s[r]==s[r-l]) r++;
  15. z[i]=r-l,r--;
  16. }
  17. else {
  18. int k=i-l;
  19. if (z[k]<r-i+1) z[i]=z[k];
  20. else {
  21. l=i;
  22. while (r<N && t[r]==t[r-l] && s[r]==s[r-l]) r++;
  23. z[i]=r-l,r--;
  24. }
  25. }
  26. }
  27. }
  28. int main(){
  29. char tmp[5],last='$';
  30. scanf("%d%d",&n,&m);
  31. for (int i=1;i<=n;i++) {
  32. int x;
  33. scanf("%d",&t1[i]);
  34. scanf("%s",tmp);
  35. if (tmp[1]==last) i--,n--,t1[i]+=t1[i+1];
  36. s1[i]=tmp[1];
  37. last=tmp[1];
  38. }
  39. last='$';
  40. for (int i=1;i<=m;i++) {
  41. scanf("%d",&t2[i]);
  42. scanf("%s",tmp);
  43. if (tmp[1]==last) i--,m--,t2[i]+=t2[i+1];
  44. s2[i]=tmp[1];
  45. last=tmp[1];
  46. }
  47. if (m==1) {
  48. for (int i=1;i<=n;i++) {
  49. if (s1[i]!=s2[1]) continue;
  50. if (t1[i]<t2[1]) continue;
  51. ans+=(t1[i]-t2[1]+1);
  52. }
  53. printf("%lld",ans);
  54. return 0;
  55. }
  56. for (int i=2;i<m;i++)
  57. s[i-2]=s2[i],t[i-2]=t2[i];
  58. for (int i=1;i<=n;i++)
  59. s[i+m-2]=s1[i],t[i+m-2]=t1[i];
  60. get_z();
  61. for (int i=m-1;i<=m+n-2;i++) {
  62. if (z[i]!=m-2) continue;
  63. if (s[i-1]!=s2[1]) continue;
  64. if (s[i-2+m]!=s2[m]) continue;
  65. if (t[i-1]<t2[1]) continue;
  66. if (t[i-2+m]<t2[m]) continue;
  67. ans++;
  68. }
  69. printf("%lld",ans);
  70. return 0;
  71. }

发表评论

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

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

相关阅读

    相关 Codeforces 496D

    题意 -------------------- 进行若干场比赛,每次比赛两人对决,赢的人得到1分,输的人不得分,先得到t分的人获胜,开始下场比赛,某个人率先赢下s场比赛