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

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
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;i1;
        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;iif(nxt[p][buf[i]] == -1)
                nxt[p][buf[i]] = newnode();
            p = nxt[p][buf[i]];
        }   
        if(flg) isEnd[p] |= (1<else    end[p] |= (1<1<void build() {
        queue<int> que;
        fail[root] = root;
        for(int i=0;iif(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;iif(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;i1;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;iscanf(" %s", s[i]);
            len = strlen(s[i]);
            for(int j=0;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;j1;j++)
                jugGapAndInsert(i, j);
        }
        ac.build();
        //ac.debug();

        dp[0][0][0] = 1;
        for(int i=0, nxt, tmpNxt, status, isEnd;ifor(int j=0;jfor(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<1][nxt][S|status] += dp[i][j][S]) %= mod;
        }
        long long ans = 0;
        for(int j=0;j1<1]) %= mod;
        printf("%lld\n", ans);
    }
}

你可能感兴趣的:(HDU,Multi-Univ)