2017 多校5 Rikka with String(ac自动机+dp)

题意:

Yuta has \(n\) \(01\) strings \(s_i\), and he wants to know the number of \(01\) antisymmetric strings of length \(2L\) which contain all given strings \(s_i\) as continuous substrings.

A \(01\) string \(s\) is antisymmetric if and only if \(s[i]≠s[|s|−i+1]\) for all \(i∈[1,|s|]\).

题解:

如果没有反对称串的限制,直接求一个长度为 \(L\) 的 \(01\) 串满足所有给定串都出现过,那么是一个经典的 \(AC\) 自动机的问题,状态 $f[i][j][S] $表示长度为 \(i\),目前在 \(AC\) 自动机的节点 \(j\) 上,已经出现的字符串集合为 \(S\) 的方案数,然后直接转移即可,时间复杂度\(O(2^{n}L\sum |s|)\)

​​

然后如果不考虑有串跨越中轴线,那么可以预处理所有正串的$AC $自动机和所有反串(即原串左右翻转)的 \(AC\) 自动机,然后从中间向两边 \(DP\),每一次枚举右侧下一个字符是 \(0\) 还是 \(1\),那么另一侧一定是另外一个字符。状态 \(f[i][j][k][S]\) 表示长度为 \(2i\),目前右半边在正串 \(AC\) 自动机的节点 \(j\) 上,左半边的反串在反串 \(AC\) 自动机的节点 \(k\) 上,已经出现的字符串集合为 \(S\) 的方案数,然后直接转移,时间复杂度 \(O(2^nL(\sum |s|)^2)\)。

现在考虑有串跨越中轴线,可以先爆枚从中间开始左右各 \(\max|s|-1\)个字符,统计出哪些串以及出现了。对于之后左右扩展出去的字符来说,肯定没有经过的它们的字符串跨越中轴线,因此可以以爆枚的结果为 \(DP\) 的初始值,从第 \(\max|s|\) 个字符开始 \(DP\)。

时间复杂度 \(O(2^nL(\sum |s|)^2+\max|s|2^{\max|s|})\)。

之前做过一道ac自动机+dp题,状态定义简直一模一样,这道题一直卡在不知道怎么处理跨越中轴线的字符串,我是只建了一个自动机,把正串和翻转的反串都插在里面,然后从左开始往右走到离中间只剩20个字符,这样的做法到这里,我只能针对Trie图上的每一个结点爆枚\(O(2^{20})\)次,所以需要再开一个自动机只把正串插在里面,以供跨越中轴的来计算,开始也想过从中间往两边走,似乎不好处理就没有仔细想了。

看了题解真是恍然大悟啊,从中间往两边,这样就只需要做一次爆枚就可以把Trie图所有节点的初始值都处理出来了,然后接着dp就好了,题解的做法开四维数组爆内存了,只能把最后的dp写成递推形式,优化成滚动数组过去,好麻烦啊,看了其它人代码内存没这么高,也不知道是什么写法。

然后突然想到了另一种做法,不需要往两边dp,往一边就可以了,我从中轴的右边\(max|s|\)字符开始走,把正串和翻转的反串都插在这个自动机里,然后爆枚就是从中轴左边的\(max|s|-1\)个字符到右边的\(max|s|-1\)个字符,这样也只需要一次就可以预处理所有初始值,这样最后dp不用写递推,dfs就好了,好写多,也比题解要快一些。

贴一下两份代码

往两边dp

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. #define P pair<int,int>
  4. #define ls(i) seg[i].lc
  5. #define rs(i) seg[i].rc
  6. using namespace std;
  7. const int mod = 998244353;
  8. const int SIZE = 2;
  9. const int MAXNODE = 130;
  10. int read(){
  11. int x = 0;
  12. char c = getchar();
  13. while(c < '0' || c > '9') c = getchar();
  14. while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
  15. return x;
  16. }
  17. int n,L;
  18. char s[7][22];
  19. int w[110];
  20. int f[2][130][130][1<<6];
  21. struct AC{
  22. int ch[MAXNODE][SIZE];
  23. int f[MAXNODE],last[MAXNODE],val[MAXNODE];
  24. int sz;
  25. void init(){
  26. sz = 1;
  27. memset(ch[0], 0, sizeof ch[0]);
  28. }
  29. int idx(char c){
  30. return c - '0';
  31. }
  32. void _insert(char* s, int v){
  33. int u = 0,len = strlen(s);
  34. for(int i = 0;i < len;i++){
  35. int c = idx(s[i]);
  36. if(!ch[u][c]){
  37. memset(ch[sz], 0, sizeof ch[sz]);
  38. val[sz] = 0;
  39. ch[u][c] = sz++;
  40. }
  41. u = ch[u][c];
  42. }
  43. val[u] |= (1<<v);
  44. }
  45. void getFail(){
  46. queue<int> q;
  47. f[0] = 0;
  48. for(int c = 0;c < SIZE;c++){
  49. int u = ch[0][c];
  50. if(u){
  51. f[u] = 0;
  52. q.push(u);
  53. last[u] = 0;
  54. }
  55. }
  56. while(!q.empty()){
  57. int r = q.front();q.pop();
  58. for(int c = 0;c < SIZE;c++){
  59. int u = ch[r][c];
  60. if(!u){
  61. ch[r][c] = ch[f[r]][c];
  62. continue;
  63. }
  64. q.push(u);
  65. int v = f[r];
  66. while(v && !ch[v][c]) v = f[v];
  67. f[u] = ch[v][c];
  68. last[u] = val[f[u]]?f[u]:last[f[u]];
  69. val[u] |= val[last[u]];
  70. }
  71. }
  72. }
  73. }acL,acR;
  74. int maxs,o[22];
  75. void dfs(int i,int j,int len,int s){
  76. s |= acL.val[i];
  77. s |= acR.val[j];
  78. if(len == maxs) {
  79. int u = 0;
  80. for(int i = len - 1;i >= 0;i--){
  81. u = acR.ch[u][o[i]];
  82. s |= acR.val[u];
  83. }
  84. for(int i = 0;i < len;i++){
  85. u = acR.ch[u][!o[i]];
  86. s |= acR.val[u];
  87. }
  88. f[0][i][j][s]++;
  89. return ;
  90. }
  91. for(int c = 0;c < 2;c++) o[len] = c,dfs(acL.ch[i][c],acR.ch[j][!c],len + 1,s);
  92. }
  93. void add(int &x,int y){
  94. x += y;
  95. if(x >= mod) x -= mod;
  96. }
  97. int main(){
  98. w[0] = 1;
  99. for(int i = 1;i <= 100;i++) w[i] = 1LL * w[i-1] * 2 % mod;
  100. int T = read();
  101. while(T--){
  102. n = read(),L = read();
  103. acL.init();
  104. acR.init();
  105. maxs = 0;
  106. for(int i = 0;i < n;i++) {
  107. scanf("%s",s[i]);
  108. int len = strlen(s[i]);
  109. maxs = max(maxs,len);
  110. acR._insert(s[i],i);
  111. reverse(s[i],s[i] + len);
  112. acL._insert(s[i],i);
  113. }
  114. maxs--;
  115. maxs = min(maxs,L);
  116. int total = (1<<n) - 1;
  117. acL.getFail();
  118. acR.getFail();
  119. memset(f,0,sizeof(f));
  120. dfs(0,0,0,0);
  121. for(int l = 0;l < L - maxs;l++){
  122. int o = l % 2, oo = !o;
  123. memset(f[oo],0,sizeof(f[oo]));
  124. for(int i = 0;i < acL.sz;i++){
  125. for(int j = 0;j < acR.sz;j++) {
  126. for(int s = 0;s <= total;s++){
  127. if(f[o][i][j][s]){
  128. for(int c = 0;c < 2;c++){
  129. int ni = acL.ch[i][c],nj = acR.ch[j][!c];
  130. add(f[oo][ni][nj][s | acL.val[ni] | acR.val[nj]],f[o][i][j][s]);
  131. }
  132. }
  133. }
  134. }
  135. }
  136. }
  137. int ans = 0;
  138. int o = (L - maxs) % 2;
  139. for(int i = 0;i < acL.sz;i++)
  140. for(int j = 0;j < acR.sz;j++) add(ans,f[o][i][j][total]);
  141. printf("%d\n",ans);
  142. }
  143. return 0;
  144. }

往右边走

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. #define P pair<int,int>
  4. #define ls(i) seg[i].lc
  5. #define rs(i) seg[i].rc
  6. using namespace std;
  7. const int mod = 998244353;
  8. const int SIZE = 2;
  9. const int MAXNODE = 300;
  10. const int MAXN = MAXNODE;
  11. int read(){
  12. int x = 0;
  13. char c = getchar();
  14. while(c < '0' || c > '9') c = getchar();
  15. while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
  16. return x;
  17. }
  18. int n,L;
  19. char s[7][22];
  20. int w[110];
  21. int f[250][110][1<<6];
  22. struct AC{
  23. int ch[MAXNODE][SIZE];
  24. int f[MAXNODE],last[MAXNODE],val[MAXNODE];
  25. int sz;
  26. void init(){
  27. sz = 1;
  28. memset(ch[0], 0, sizeof ch[0]);
  29. }
  30. int idx(char c){
  31. return c - '0';
  32. }
  33. void _insert(char* s, int v){
  34. int u = 0,len = strlen(s);
  35. for(int i = 0;i < len;i++){
  36. int c = idx(s[i]);
  37. if(!ch[u][c]){
  38. memset(ch[sz], 0, sizeof ch[sz]);
  39. val[sz] = 0;
  40. ch[u][c] = sz++;
  41. }
  42. u = ch[u][c];
  43. }
  44. val[u] |= (1<<v);
  45. }
  46. void getFail(){
  47. queue<int> q;
  48. f[0] = 0;
  49. for(int c = 0;c < SIZE;c++){
  50. int u = ch[0][c];
  51. if(u){
  52. f[u] = 0;
  53. q.push(u);
  54. last[u] = 0;
  55. }
  56. }
  57. while(!q.empty()){
  58. int r = q.front();q.pop();
  59. for(int c = 0;c < SIZE;c++){
  60. int u = ch[r][c];
  61. if(!u){
  62. ch[r][c] = ch[f[r]][c];
  63. continue;
  64. }
  65. q.push(u);
  66. int v = f[r];
  67. while(v && !ch[v][c]) v = f[v];
  68. f[u] = ch[v][c];
  69. last[u] = val[f[u]]?f[u]:last[f[u]];
  70. val[u] |= val[last[u]];
  71. }
  72. }
  73. }
  74. }acL,acR;
  75. int maxs,o[21],total,ans;
  76. int dp(int i,int len,int s){
  77. if(s == total) return w[L - len];
  78. if(len == L) return s == total;
  79. int &res = f[i][len][s];
  80. if(res != -1) return res;
  81. res = 0;
  82. for(int c = 0;c < 2;c++) res = (res + dp(acR.ch[i][c],len + 1,(s | acR.val[acR.ch[i][c]])))%mod;
  83. return res;
  84. }
  85. void dfs(int len){
  86. if(len == maxs) {
  87. int u = 0,v = 0,s = 0;
  88. for(int i = len - 1;i >= 0;i--){
  89. u = acL.ch[u][!o[i]];
  90. v = acR.ch[v][!o[i]];
  91. s |= acL.val[u];
  92. }
  93. for(int i = 0;i < len;i++){
  94. u = acL.ch[u][o[i]];
  95. v = acR.ch[v][o[i]];
  96. s |= acL.val[u];
  97. }
  98. ans = (ans + dp(v,len,s))%mod;
  99. return ;
  100. }
  101. for(int c = 0;c < 2;c++) o[len] = c,dfs(len + 1);
  102. }
  103. int main(){
  104. w[0] = 1;
  105. for(int i = 1;i <= 100;i++) w[i] = 1LL * w[i-1] * 2 % mod;
  106. int T = read();
  107. while(T--){
  108. n = read(),L = read();
  109. acL.init();
  110. acR.init();
  111. maxs = 0;
  112. for(int i = 0;i < n;i++) {
  113. scanf("%s",s[i]);
  114. int len = strlen(s[i]);
  115. maxs = max(maxs,len);
  116. acR._insert(s[i],i);
  117. acL._insert(s[i],i);
  118. reverse(s[i],s[i] + len);
  119. for(int j = 0;j < len;j++) if(s[i][j] == '1') s[i][j] = '0';else s[i][j] = '1';
  120. acR._insert(s[i],i);
  121. }
  122. ans = 0;
  123. maxs--;
  124. maxs = min(L,maxs);
  125. total = (1<<n) - 1;
  126. acL.getFail();
  127. acR.getFail();
  128. memset(f,-1,sizeof(f));
  129. dfs(0);
  130. printf("%d\n",ans);
  131. }
  132. return 0;
  133. }

2017 多校5 Rikka with String的更多相关文章

  1. 2017 多校5 hdu 6093 Rikka with Number

    2017 多校5 Rikka with Number(数学 + 数位dp) 题意: 统计\([L,R]\)内 有多少数字 满足在某个\(d(d>=2)\)进制下是\(d\)的全排列的 \(1 & ...

  2. 2016暑假多校联合---Rikka with Sequence (线段树)

    2016暑假多校联合---Rikka with Sequence (线段树) Problem Description As we know, Rikka is poor at math. Yuta i ...

  3. hdu.5202.Rikka with string(贪心)

    Rikka with string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others ...

  4. 【Hihocoder1413】Rikka with String(后缀自动机)

    [Hihocoder1413]Rikka with String(后缀自动机) 题面 Hihocoder 给定一个小写字母串,回答分别把每个位置上的字符替换为'#'后的本质不同的子串数. 题解 首先横 ...

  5. HDU 6086 Rikka with String

    Rikka with String http://acm.hdu.edu.cn/showproblem.php?pid=6086 题意: 求一个长度为2L的,包含所给定的n的串,并且满足非对称. 分析 ...

  6. 2017 多校4 Wavel Sequence

    2017 多校4 Wavel Sequence 题意: Formally, he defines a sequence \(a_1,a_2,...,a_n\) as ''wavel'' if and ...

  7. 2017 多校4 Security Check

    2017 多校4 Security Check 题意: 有\(A_i\)和\(B_i\)两个长度为\(n\)的队列过安检,当\(|A_i-B_j|>K\)的时候, \(A_i和B_j\)是可以同 ...

  8. 2017 多校3 hdu 6061 RXD and functions

    2017 多校3 hdu 6061 RXD and functions(FFT) 题意: 给一个函数\(f(x)=\sum_{i=0}^{n}c_i \cdot x^{i}\) 求\(g(x) = f ...

  9. 2017 多校2 hdu 6053 TrickGCD

    2017 多校2 hdu 6053 TrickGCD 题目: You are given an array \(A\) , and Zhu wants to know there are how ma ...

随机推荐

  1. 基于 win7下虚拟机的 GNSS-SDR安装过程

    最近在安装 GNSS-SDR软件时,遇到了很多问题,这里回顾了我的安装过程,罗列了所遇到的问题和解决办法.希望后来者不要再踩这些坑了! 首先,在官方文档中看到,GNSS-SDR目前并不支持直接在 Wi ...

  2. 2.1 <script>元素【JavaScript高级程序设计第三版】

    向 HTML 页面中插入 JavaScript 的主要方法,就是使用<script>元素.这个元素由 Netscape 创造并在 Netscape Navigator 2 中首先实现.后来 ...

  3. laravels -- Swoole加速php

    LaravelS是一个胶水项目,用于快速集成Swoole到Laravel,然后赋予它们更好的性能.更多可能性. 环境 : ubuntu16 + nginx + php7.1 + LaravelS搭建高 ...

  4. POJ:2229-Sumsets(完全背包的优化)

    题目链接:http://poj.org/problem?id=2229 Sumsets Time Limit: 2000MS Memory Limit: 200000K Total Submissio ...

  5. [Bzoj4289]PA2012 Tax(Dijkstra+技巧建图)

    Description 给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价.起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边 ...

  6. 16,Flask-Migrate

    终于到了Flask-Migrate,之前在学习Flask-SQLAlchemy的时候,Flask支持 makemigration / migrate 吗? 答案在这里该诉你,如果你同时拥有两个三方组件 ...

  7. java程序——随机数求和

    设计思路:用随机算法随机生成10个数(0~100),循环填充一个数组,然后在循环中显示数组内容,接着用一个循环计算数组元素的和,将结果也显示在消息框中. 流程图: 源代码: package test; ...

  8. JS 金钱格式化

    JavaScript Money Format(用prototype对Number进行扩展) Number.prototype.formatMoney = function (places, symb ...

  9. Postman-进阶(2)

    Postman-进阶(2) Postman-简单使用 Postman-进阶使用 Postman-CI集成Jenkins 管理请求 保存请求-添加“打开百度首页请求” 设置请求方式为Get,地址为www ...

  10. iOS算法笔记-快速排序-OC实现

    快速排序(Quicksort)是对冒泡排序的一种改进. 快速排序由C. A. R. Hoare在1962年提出.它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另 ...