[关闭]
@Livingston 2021-05-05T02:42:35.000000Z 字数 2717 阅读 579

[JZOJ3159] [P5254]「JSOI2013」『字符串哈希』『枚举』广告计划

题解 字符串哈希 枚举
可能更好的阅读体验

题目

如今,在建筑的墙面上或者篱笆桩的表面上涂上一些广告,是一种新的吸引眼球的方法。

现在,小G 运营的一家小公司,决定也试着这样做做广告。小G 在他的篱笆桩上腾出了一些地方供广告使用。每一个篱笆桩都是一个水平的1*1*L 的4 棱柱,其中有一个1*L 的面是可以做广告的。1*L 的面上划出了L 个1*1 的小正方形(更具体地说是连续L 个水平排列的正方形),每个正方形内写上一个字母。

​ 时间久了,广告做多了难免会出现一些比较麻烦的情况,比如计划改变或者制作出错,因此小G 的仓库里面积累了好多没有用的,上面已经写上L 个字母的篱笆桩。(所有的篱笆桩的大小都是一样的,他们唯一的区别仅仅在于上面写了什么字母)。

​ 小G 决定对于这些篱笆桩进行重新利用,并且有了一些新的想法。

​ 如果将这些篱笆桩竖直的叠放起来,并且依次从左往右,对于每一个篱笆桩顺次从上到下读出上面的字母,那么我们可以得到一些新的比较长的单词,如下图:

​ 这些新的单词能满足小G 的一些新的需要。当然,基于美学考虑,小G 是不允许你删改篱笆桩上已经写上的字母的。

​ 我们更具体地描述这个过程。我们将K 个长度为L 的篱笆桩叠在一起,可以得到一个写有K行L 列共K*L 个字母的面,每一个字母都在对应的唯一的格子里。我们从左上角开始依次向下读出每一个字母可以得到一个字母的序列,比如上图中的这个例子,那么我们读出的结果就是“TOEIIZENITKN”。如果,这个串中有我们所需要的单词,那么显然我们只需要将一些格子刷白,就可以得到我们所需要的了。举个例子,比如小G 想要给圣彼得堡的足球队泽尼特队做个广告,那么很显然只要按照上图中的做法就可以达到小G 想要的效果了。

​ 现在小G已经想好了要做怎样的广告,同时也提供给你了小G仓库中的篱笆桩的类型的描述,你可以认为每一种类型的篱笆桩都是有无数个的。现在小G 想知道至少需要多少个篱笆桩叠起来才可以做出小G 想要的广告。

,

题解

题目大意:有种长度为的字符串和一个匹配串,每种都有无数个,现选出字符串,从上到下排成一个的字符矩阵,从左上角开始,从上到下,从左到右顺序写下各个字符,得出一个长度为的字符串,且的子串,最小化和一种排列方案

考虑字符串哈希,将所有子串用27进制压缩存到哈希表中(即每个字符串的任意位置开头和任意位置结尾),同时记录每个串所在的字符串的编号和开头所在的位置。

从小到大枚举,那么第一个合法就是答案。找出当前下匹配串被分成的个小串

如:

3个小串分别是

将这个小串以上述方法压缩,在哈希表中查找

若第个小串在第个字符串中,开头在第列,那么,表示第个小串的开头在第列可以在第个字符串中找到

再枚举的第一个子串的开头的行,列,在中查找,如果第或者列都有对应的值,那么就是合法答案

也就是说,在这种情况下,如果一些和另一些都有值(表示的第个子串),那么就合法(还是

Code

  1. #include<cstdio>
  2. #include<cstring>
  3. #define md 2999999
  4. #define hashmd 5100001
  5. using namespace std;
  6. int n,l,m,id,now,num,mi[105],pos[505001][2],nxt[505001],head[5100001],hs[5100001],bj[205][205],c[205][205];
  7. bool flag;
  8. char s[105][105],ss[205];
  9. void add(int x,int i,int j)
  10. {
  11. pos[++num][0]=i;pos[num][1]=j;
  12. nxt[num]=head[x];
  13. head[x]=num;
  14. }
  15. void hash(int x,int i,int j)
  16. {
  17. int p=x;
  18. while (hs[p])
  19. {
  20. if (hs[p]==x)
  21. {
  22. add(p,i,j);
  23. return;
  24. }
  25. p=(p+1)%hashmd;
  26. }
  27. hs[p]=x;
  28. add(p,i,j);
  29. }
  30. void find(int x,int r)
  31. {
  32. int p=x;
  33. while (hs[p])
  34. {
  35. if (hs[p]==x)
  36. {
  37. for (int i=head[x];i;i=nxt[i])
  38. c[r][pos[i][1]]=pos[i][0],bj[r][pos[i][1]]=id;
  39. return;
  40. }
  41. p=(p+1)%hashmd;
  42. }
  43. }
  44. int main()
  45. {
  46. mi[0]=1;
  47. for (int i=1;i<=100;++i)
  48. mi[i]=mi[i-1]*27%md;
  49. scanf("%d%d",&n,&l);
  50. for (int i=1;i<=n;++i)
  51. {
  52. scanf("%s",s[i]+1);
  53. for (int j=1;j<=l;++j)
  54. {
  55. int x=1;
  56. for (int k=j;k<=l;++k)
  57. {
  58. x=(x+(s[i][k]-'a'+1)*mi[k-j+1]%md)%md;
  59. hash(x,i,j);
  60. }
  61. }
  62. }
  63. scanf("%s",ss+1);
  64. m=strlen(ss+1);
  65. for (int k=1;k<=m;++k)
  66. {
  67. ++id;
  68. for (int j=1;j<=k;++j)
  69. {
  70. int x=1,ln=0;
  71. now=j;
  72. while (now<=m)
  73. {
  74. x=(x+(ss[now]-'a'+1)*mi[++ln]%md)%md;
  75. now+=k;
  76. }
  77. find(x,j);
  78. }
  79. for (int i=1;i<=k;++i)
  80. {
  81. int h=k-i+1;
  82. for (int j=1;j<=l;++j)
  83. {
  84. flag=true;
  85. for (int u=1;u<=k;++u)
  86. {
  87. if ((u<=h&&bj[u][j]<id)||(u>h&&bj[u][j+1]<id))
  88. {
  89. flag=false;
  90. break;
  91. }
  92. }
  93. if (flag)
  94. {
  95. printf("%d\n",k);
  96. for (int u=h+1;u<=k;++u) printf("%d ",c[u][j+1]);
  97. for (int u=1;u<=h;++u) printf("%d ",c[u][j]);
  98. return 0;
  99. }
  100. }
  101. }
  102. }
  103. printf("-1\n");
  104. return 0;
  105. }

感谢LZA的博客提供的帮助,ORZ:

https://blog.csdn.net/qq_39565901/article/details/87472261

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注