hdu6086ac自动机+dp

xiaoxiao2021-02-28  104

Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them: Yuta has  n   01  strings  si , and he wants to know the number of  01  antisymmetric strings of length  2L  which contain all given strings  si  as continuous substrings. A  01  string  s  is antisymmetric if and only if  s[i]s[|s|i+1]  for all  i[1,|s|] . It is too difficult for Rikka. Can you help her? In the second sample, the strings which satisfy all the restrictions are  000111,001011,011001,100110 .   Input The first line contains a number  t(1t5) , the number of the testcases.  For each testcase, the first line contains two numbers  n,L(1n6,1L100) .  Then  n  lines follow, each line contains a  01  string  si(1|si|20) .   Output For each testcase, print a single line with a single number -- the answer modulo 998244353.   Sample Input 2 2 2 011 001 2 3 011 001   Sample Output 1 4   Source 2017 Multi-University Training Contest - Team 5 多校还是有收获的,又学到了新姿势,ac自动机+dp的一类题 题意:给出m个单词,要构造出长度为2*L的包含这全部m个单词的字符串,并且保证这个字符串非对称,其中字符只包括0和1,问一共有多少种构造方法。 思路:看了很多博客,先做了个简单版的hdu2825(建议先做一下),2825没有非对称这个条件,由于有非对称这个条件,所以我们只需要构造前一半长度为L的字符串就确定了整个串,字符串在构造的串种可能出现的位置有三种 1.出现在字符串的前一半 2出现在字符串的后一半 3.一半在前边,一半在后边 前两种情况都比较好处理, 1.把给的字符串插入到ac自动机 2.把给的字符串的反串插入到ac自动机(在字符串的后一半出现,那么其反串就会出现在前边) 比较困难的是第三种情况,我们把字符串分成两部分,但是由于必须还得是非对称串,所以前后部分是对称的要去掉,还有一点就是两边长度不一定相同,我们必须要把长度短的补长,例如: 10101 10   101,当这样分配是我们不能直接将10插入到ac自动机,我们要将10补齐为100插入到ac自动机 这个题还有一点就是卡空间,题解中给出的解决方法是将L%2,因为dp过程中前边计算过的对后边没什么用了 ac代码: #include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXNODE = 2500; const int SIGMA_SIZE = 2; const LL MOD = 998244353; struct ACauto { int next[MAXNODE][SIGMA_SIZE], fail[MAXNODE], end1[MAXNODE], end2[MAXNODE]; int root,sz; int newnode() { for (int i = 0; i < SIGMA_SIZE; i++) next[sz][i] = -1; end1[sz] = 0; end2[sz++] = 0; return sz - 1; } void init() { sz = 0; root = newnode(); } void insert1(char *buf, int id) { int len = strlen(buf); int now = root; for (int i = 0; i < len; i++) { if (next[now][buf[i] - '0'] == -1) next[now][buf[i] - '0'] = newnode(); now = next[now][buf[i] - '0']; } end1[now] |= (1 << id); } void insert2(char *buf, int id) { int len = strlen(buf); int now = root; for (int i = 0; i < len; i++) { if (next[now][buf[i] - '0'] == -1) next[now][buf[i] - '0'] = newnode(); now = next[now][buf[i] - '0']; } end2[now] |= (1 << id); } void build() { queue <int> Q; fail[root] = root; for (int i = 0; i < SIGMA_SIZE; i++) { if (next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } } while (!Q.empty()) { int now = Q.front(); Q.pop(); end1[now] |= end1[fail[now]]; end2[now] |= end2[fail[now]]; for (int i = 0; i < SIGMA_SIZE; i++) { if (next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; Q.push(next[now][i]); } } } } } ac; int n, L; char s[30], t[30], str[60]; LL dp[2][2500][(1 << 6) + 10]; int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &L); ac.init(); for (int i = 0; i < n; i++) { scanf("%s", s); ac.insert1(s, i); int len = strlen(s); for (int j = 0; j < len; j++) t[j] = s[len - 1 - j] == '0' ? '1' : '0'; t[len] = '\0'; ac.insert1(t, i); for (int j = 0; j < len - 1; j++) { string s1 = "", s2 = ""; for (int k = j; k >= 0; k--) s1 += s[k]; for (int k = j + 1; k < len; k++) s2 += s[k]; bool flag = true; for (int k = 0; k < (int)s1.length() && k < (int)s2.length(); k++) { if (s1[k] == s2[k]) { flag = false; break; } } if (!flag) continue; reverse(s1.begin(), s1.end()); for (int k = (j + 1) * 2; k < len; k++) { s1 = (s[k] == '0' ? '1' : '0') + s1; } strcpy(str, s1.c_str()); ac.insert2(str, i); } } ac.build(); memset(dp, 0, sizeof(dp)); dp[0][0][0] = 1; for (int i = 0; i < L; i++) { for (int j = 0; j < ac.sz; j++) { for (int S = 0; S < (1 << n); S++) { if (dp[i%2][j][S] <= 0) continue; for (int k = 0; k < SIGMA_SIZE; k++) { int ni = i + 1, nj = ac.next[j][k], nS = S | ac.end1[nj]; if (i == L - 1) nS |= ac.end2[nj]; dp[ni%2][nj][nS] = (dp[ni%2][nj][nS] + dp[i%2][j][S]) % MOD; } dp[i%2][j][S] = 0; } } } LL ans = 0; for (int i = 0; i < ac.sz; i++) { ans = (ans + dp[L%2][i][(1 << n) - 1]) % MOD; } printf("%I64d\n", ans); } return 0; }
转载请注明原文地址: https://www.6miu.com/read-83545.html

最新回复(0)