zoukankan      html  css  js  c++  java
  • 2017 多校5 Rikka with String

    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}Lsum |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

    #include<bits/stdc++.h>
    #define LL long long
    #define P pair<int,int>
    #define ls(i) seg[i].lc
    #define rs(i) seg[i].rc
    using namespace std;
    const int mod = 998244353;
    const int SIZE = 2;
    const int MAXNODE = 130;
    int read(){
        int x = 0;
        char c = getchar();
        while(c < '0' || c > '9') c = getchar();
        while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
        return x;
    }
    int n,L;
    char s[7][22];
    int w[110];
    int f[2][130][130][1<<6];
    struct AC{
        int ch[MAXNODE][SIZE];
        int f[MAXNODE],last[MAXNODE],val[MAXNODE];
        int sz;
        void init(){
            sz = 1;
            memset(ch[0], 0, sizeof ch[0]);
        }
        int idx(char c){
            return c - '0';
        }
        void _insert(char* s, int v){
            int u = 0,len = strlen(s);
            for(int i = 0;i < len;i++){
                int c = idx(s[i]);
                if(!ch[u][c]){
                    memset(ch[sz], 0, sizeof ch[sz]);
                    val[sz] = 0;
                    ch[u][c] = sz++;
                }
                u = ch[u][c];
            }
            val[u] |= (1<<v);
        }
        void getFail(){
            queue<int> q;
            f[0] = 0;
            for(int c = 0;c < SIZE;c++){
                int u = ch[0][c];
                if(u){
                    f[u] = 0;
                    q.push(u);
                    last[u] = 0;
                }
            }
            while(!q.empty()){
                int r = q.front();q.pop();
                for(int c = 0;c < SIZE;c++){
                    int u = ch[r][c];
                    if(!u){
                        ch[r][c] = ch[f[r]][c];
                        continue;
                    }
                    q.push(u);
                    int v = f[r];
                    while(v && !ch[v][c]) v = f[v];
                    f[u] = ch[v][c];
                    last[u] = val[f[u]]?f[u]:last[f[u]];
                    val[u] |= val[last[u]];
                }
            }
        }
    }acL,acR;
    int maxs,o[22];
    void dfs(int i,int j,int len,int s){
        s |= acL.val[i];
        s |= acR.val[j];
        if(len == maxs) {
            int u = 0;
            for(int i = len - 1;i >= 0;i--){
                u = acR.ch[u][o[i]];
                s |= acR.val[u];
            }
            for(int i = 0;i < len;i++){
                u = acR.ch[u][!o[i]];
                s |= acR.val[u];
            }
            f[0][i][j][s]++;
            return ;
        }
        for(int c = 0;c < 2;c++) o[len] = c,dfs(acL.ch[i][c],acR.ch[j][!c],len + 1,s);
    }
    void add(int &x,int y){
        x += y;
        if(x >= mod) x -= mod;
    }
    int main(){
        w[0] = 1;
        for(int i = 1;i <= 100;i++) w[i] = 1LL * w[i-1] * 2 % mod;
        int T = read();
        while(T--){
            n = read(),L = read();
            acL.init();
            acR.init();
            maxs = 0;
            for(int i = 0;i < n;i++) {
                    scanf("%s",s[i]);
                    int len = strlen(s[i]);
                    maxs = max(maxs,len);
                    acR._insert(s[i],i);
                    reverse(s[i],s[i] + len);
                    acL._insert(s[i],i);
            }
            maxs--;
            maxs = min(maxs,L);
            int total = (1<<n) - 1;
            acL.getFail();
            acR.getFail();
            memset(f,0,sizeof(f));
            dfs(0,0,0,0);
            for(int l = 0;l < L - maxs;l++){
                int o = l % 2, oo = !o;
                memset(f[oo],0,sizeof(f[oo]));
                for(int i = 0;i < acL.sz;i++){
                    for(int j = 0;j < acR.sz;j++) {
                        for(int s = 0;s <= total;s++){
                                if(f[o][i][j][s]){
                                    for(int c = 0;c < 2;c++){
                                        int ni = acL.ch[i][c],nj = acR.ch[j][!c];
                                        add(f[oo][ni][nj][s | acL.val[ni] | acR.val[nj]],f[o][i][j][s]);
                                    }
                                }
                        }
                    }
                }
            }
            int ans = 0;
            int o = (L - maxs) % 2;
            for(int i = 0;i < acL.sz;i++)
                    for(int j = 0;j < acR.sz;j++) add(ans,f[o][i][j][total]);
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    往右边走

    #include<bits/stdc++.h>
    #define LL long long
    #define P pair<int,int>
    #define ls(i) seg[i].lc
    #define rs(i) seg[i].rc
    using namespace std;
    const int mod = 998244353;
    const int SIZE = 2;
    const int MAXNODE = 300;
    const int MAXN = MAXNODE;
    int read(){
        int x = 0;
        char c = getchar();
        while(c < '0' || c > '9') c = getchar();
        while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
        return x;
    }
    int n,L;
    char s[7][22];
    int w[110];
    int f[250][110][1<<6];
    
    struct AC{
        int ch[MAXNODE][SIZE];
        int f[MAXNODE],last[MAXNODE],val[MAXNODE];
        int sz;
        void init(){
            sz = 1;
            memset(ch[0], 0, sizeof ch[0]);
        }
        int idx(char c){
            return c - '0';
        }
        void _insert(char* s, int v){
            int u = 0,len = strlen(s);
            for(int i = 0;i < len;i++){
                int c = idx(s[i]);
                if(!ch[u][c]){
                    memset(ch[sz], 0, sizeof ch[sz]);
                    val[sz] = 0;
                    ch[u][c] = sz++;
                }
                u = ch[u][c];
            }
            val[u] |= (1<<v);
        }
        void getFail(){
            queue<int> q;
            f[0] = 0;
            for(int c = 0;c < SIZE;c++){
                int u = ch[0][c];
                if(u){
                    f[u] = 0;
                    q.push(u);
                    last[u] = 0;
                }
            }
            while(!q.empty()){
                int r = q.front();q.pop();
                for(int c = 0;c < SIZE;c++){
                    int u = ch[r][c];
                    if(!u){
                        ch[r][c] = ch[f[r]][c];
                        continue;
                    }
                    q.push(u);
                    int v = f[r];
                    while(v && !ch[v][c]) v = f[v];
                    f[u] = ch[v][c];
                    last[u] = val[f[u]]?f[u]:last[f[u]];
                    val[u] |= val[last[u]];
                }
            }
        }
    }acL,acR;
    int maxs,o[21],total,ans;
    int dp(int i,int len,int s){
        if(s == total) return w[L - len];
        if(len == L) return s == total;
        int &res = f[i][len][s];
        if(res != -1) return res;
        res = 0;
        for(int c = 0;c < 2;c++) res = (res + dp(acR.ch[i][c],len + 1,(s |  acR.val[acR.ch[i][c]])))%mod;
        return res;
    }
    void dfs(int len){
        if(len == maxs) {
            int u = 0,v = 0,s = 0;
            for(int i = len - 1;i >= 0;i--){
                u = acL.ch[u][!o[i]];
                v = acR.ch[v][!o[i]];
                s |= acL.val[u];
            }
            for(int i = 0;i < len;i++){
                u = acL.ch[u][o[i]];
                v = acR.ch[v][o[i]];
                s |= acL.val[u];
            }
            ans = (ans +  dp(v,len,s))%mod;
            return ;
        }
        for(int c = 0;c < 2;c++) o[len] = c,dfs(len + 1);
    }
    int main(){
        w[0] = 1;
        for(int i = 1;i <= 100;i++) w[i] = 1LL * w[i-1] * 2 % mod;
        int T = read();
        while(T--){
            n = read(),L = read();
            acL.init();
            acR.init();
            maxs = 0;
            for(int i = 0;i < n;i++) {
                    scanf("%s",s[i]);
                    int len = strlen(s[i]);
                    maxs = max(maxs,len);
                    acR._insert(s[i],i);
                    acL._insert(s[i],i);
                    reverse(s[i],s[i] + len);
                    for(int j = 0;j < len;j++) if(s[i][j] == '1') s[i][j] = '0';else s[i][j] = '1';
                    acR._insert(s[i],i);
            }
            ans = 0;
            maxs--;
            maxs = min(L,maxs);
            total = (1<<n) - 1;
            acL.getFail();
            acR.getFail();
            memset(f,-1,sizeof(f));
            dfs(0);
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    javascript异步编程系列【十】—Jscex+Easeljs制作坦克大战
    博客园分页JQuery打造的分页无刷新的Repeater
    参赛作品
    摄像机、投影、3D旋转、缩放
    javascript异步编程系列【八】Jscex版火拼俄罗斯
    javascript异步编程系列【七】扫盲,我们为什么要用Jscex
    javascript异步编程系列【五】Jscex制作愤怒的小鸟
    javascript异步编程系列【六】Jscex版愤怒的小鸟之冲锋陷阵鸟
    每周优秀代码赏析—Jscex内核【一】
    javascript异步编程系列【一】用Jscex画圆
  • 原文地址:https://www.cnblogs.com/jiachinzhao/p/7323897.html
Copyright © 2011-2022 走看看