HDU 6086 Rikka with String (AC 自动机+状压 dp, 2017 Multi-Univ Training Contest 5)

来源:互联网 发布:落英纷飞 音乐软件 编辑:程序博客网 时间:2024/06/07 22:47

Problem

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.

对给定的 n 个 01 串 si ,求一个长为 2L 的反回文串(antisymmeric strings) ,使得 n 个串均作为子串出现过。

其中反回文串指串 s 满足 sis|s|i+1

Limit

1n6

1L100

1si20

Idea

如果不考虑反回文串的问题,此题就是一个简单的 AC 自动机 + dp (当然,需要状压) 的问题。(如果不能理解,请自行搜索)。

由于有反回文的设定,故对于 2L 长的 01 串,当前 L 长已知时,后 L 长必然确定。故考虑三种情况,字符串 si 落在前 L ,在后 L 或者一部分在前 L 部分在后 L 。

  • 字符串 si 落在前 L 的问题,直接在 AC 自动机上插入原串 si
  • 字符串 si 落在后 L 的问题,该字符串在前 L 一定呈现了它本身的逆反串(即 reverse(s_i) 同时对每一位的 01 取反)在 AC 自动机上插入原串的逆反串。
  • 字符串 si 部分在前 L 部分在后 L 的问题,由于需要区分多少长在前 L ,多少在后 L ,且有部分无法满足反回文串的限制。故需多重考虑。总之,对枚举的合法的可能,构造能使得该串完整出现在该前后 L 结合部分的串 ss (具体见 jugGapAndInsert() 函数),将其加入 AC 自动机。

AC 自动机在产生 fail 指针后,直接状压 dp 。

Code

#include<bits/stdc++.h>using namespace std;char s[6][22], ss[22];const int maxn = 1000+10;const int mod = 998244353;const int CH = 3;int t, n, L, dp[101][maxn][128];struct Trie {    int nxt[maxn][CH], fail[maxn], end[maxn], isEnd[maxn];    int root, L;    void init() {        L = 0;        root = newnode();    }    int newnode() {        for(int i=0;i<CH;i++)   nxt[L][i] = -1;        isEnd[L] = 0;        end[L++] = 0;        return L-1;    }    void insert(char buf[], int len, int idx, bool flg) {        int p = root;        for(int i=0;i<len;i++) {            if(nxt[p][buf[i]] == -1)                nxt[p][buf[i]] = newnode();            p = nxt[p][buf[i]];        }           if(flg) isEnd[p] |= (1<<idx);        else    end[p] |= (1<<idx);        isEnd[p] |= (1<<idx);    }    void build() {        queue<int> que;        fail[root] = root;        for(int i=0;i<CH;i++)            if(nxt[root][i] == -1)                nxt[root][i] = root;            else                fail[nxt[root][i]] = root,                que.push(nxt[root][i]);        while(!que.empty()) {            int p = que.front();            que.pop();            for(int i=0;i<CH;i++)                 if(nxt[p][i] == -1)                    nxt[p][i] = nxt[fail[p]][i];                else {                    fail[nxt[p][i]] = nxt[fail[p]][i];                    que.push(nxt[p][i]);                }        }    }    void debug(){        for(int i = 0;i < L;i++) {            printf("id = %2d,fail = %3d,end = %3d, isEnd = %d, chi = [",i,fail[i],end[i],isEnd[i]);            for(int j = 0;j < CH;j++)                printf("%3d",nxt[i][j]);            printf("]\n");        }    }}ac;void jugGapAndInsert(int idx, int gap) {    for(int i=0;;i++) {        if(gap+i+1 == strlen(s[idx]) || gap-i < 0)  break;        if(s[idx][gap+i+1] == s[idx][gap-i])    return;    }       int len = strlen(s[idx]);    if(gap+1 > len-gap-1) {        ac.insert(s[idx], gap+1, idx, true);    } else {        for(int i=0;i<len-gap-1;i++)            ss[i] = 3 - s[idx][len-i-1];        ac.insert(ss, len-gap-1, idx, true);    }}int main(){    scanf("%d", &t);    while(t-- && scanf("%d %d", &n, &L)!=EOF)    {        ac.init();        memset(dp, 0, sizeof(dp));        for(int i=0, len;i<n;i++) {            scanf(" %s", s[i]);            len = strlen(s[i]);            for(int j=0;j<len;j++) {                s[i][j] = s[i][j] - '0' + 1;                ss[len-j-1] = 3 - s[i][j];            }            ac.insert(s[i], len, i, false);            ac.insert(ss, len, i, false);            for(int j=0;j<len-1;j++)                jugGapAndInsert(i, j);        }        ac.build();        //ac.debug();        dp[0][0][0] = 1;        for(int i=0, nxt, tmpNxt, status, isEnd;i<L;i++)        for(int j=0;j<ac.L;j++)        for(int c=1;c<=2;c++)        {               nxt = j;            status = 0, isEnd = 0;            nxt = ac.nxt[nxt][c];            tmpNxt = nxt;            while(tmpNxt != 0) {                status |= ac.end[tmpNxt];                if(i+1==L)  status |= ac.isEnd[tmpNxt];                tmpNxt = ac.fail[tmpNxt];            }            for(int S=0;S<(1<<n);S++)                (dp[i+1][nxt][S|status] += dp[i][j][S]) %= mod;        }        long long ans = 0;        for(int j=0;j<ac.L;j++)            (ans += dp[L][j][(1<<n)-1]) %= mod;        printf("%lld\n", ans);    }}
阅读全文
0 0
原创粉丝点击