zoukankan      html  css  js  c++  java
  • 字符串

    ### Description

      数据范围:(n<=6,|s_i|<=100,m<=500)

      

    Solution

    ​  场上不会在ac自动机上面跑dp的我大概失去了智商==果然字符串这块还是有点薄弱啊

      首先想一个比较好的计算方式:比较明白的一点是如果前(m)位确定了,后(m)位自然也就确定了,我们将满足条件的串分成三大类:

    (1)匹配串都在前(m)位(前半段)

    (2)匹配串都在后(m)位(后半段)

    (3)匹配串跨(m)这个位置

    ​  第三类又可以再分两类:跨(m)这个位置的串在前半段的长度比较大、在后半段的长度比较大

    ​   

      首先考虑前两类怎么计算:其实只要把正串和翻转之后再(01)反转的串都丢到ac自动机里面然后跑dp就好了

      看到这个(n)这么小,大概差不多就是用来状压的了吧,于是粗暴地令(f[i][j][k])表示确定了前(i)位,当前在(j)这个节点,当前已经包含的串状态为(k),然后直接(O(m*)自动机节点数(*2^n))暴力dp就好了

      具体一点就是对于每个节点记录一个(st[x])表示走到这个节点意味着包含了哪些字符串,预处理的时候从fail树上面从上往下传就好了(当然实现的时候并不用真的建出来,记录一下bfs序然后直接传就好了)

    ​  最后就是第三种情况,这个其实也比较好搞,对于每个自动机上的节点我们维护一个(midst[x])表示这个节点作为新串中的第(m)位可以包含到哪些匹配串,我们枚举每个匹配串(包括反串)的每一位,如果这个位置可以作为满足条件的串的第(m)位的话(说白了就是可从这个位置切开满足反对称),并且在这里切开之后满足前半段的长度更长的话(因为枚举的字符串中既有正串也有反串,所以只要保证一种情况就可以将(3)中的两小类不重不漏地算进去了),我们将其加入对应的自动机节点的(midst[x])里面去,然后同理这个(midst)也要下传,方式和上面的(st)一样

    ​  最后查答案的时候枚举(f)的后两维,如果说当前的状态(k|midst[j]=)满状态的话,就将(f[m][j][k])加入答案中

      
      代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=15,L=110,M=510,MOD=998244353;
    char s[N][L];
    int n,m,ans,all;
    int St(int x){if (x>n) x-=n; return 1<<x-1;}
    bool in(int st,int x){return st>>x-1&1;}
    int mul(int x,int y){return 1LL*x*y%MOD;}
    int plu(int x,int y){return 1LL*x+y-(1LL*x+y>=MOD?MOD:0);}
    namespace Ac{/*{{{*/
        const int N=1210,C=2,ST=(1<<6)+10;
        queue<int> q;
        int ch[N][C],fail[N],st[N],lis[N],midst[N];
        int f[M][N][ST];
        int tot,rt;
        void init(){tot=0; rt=0;}
        void debug(){
            for (int i=rt;i<=tot;++i) printf("%d ",st[i]); printf("
    ");
        }
        int newnode(){
            fail[++tot]=0; st[tot]=0;
            for (int i=0;i<C;++i) ch[tot][i]=0;
            return tot;
        }
        void insert(int id){
            int now=rt,c,len=strlen(s[id]);
            for (int i=0;i<len;++i){
                c=s[id][i]-'0';
                if (!ch[now][c]) ch[now][c]=newnode();
                now=ch[now][c];
            }
            st[now]|=St(id);
        }
        void build(){
            int u,v;
            while (!q.empty()) q.pop();
            q.push(rt); lis[0]=0;
            while (!q.empty()){
                v=q.front(); q.pop(); lis[++lis[0]]=v;
                for (int i=0;i<C;++i){
                    if (!ch[v][i]){
                        ch[v][i]=ch[fail[v]][i];
                        continue;
                    }
                    if (v==rt)
                        fail[ch[v][i]]=rt;
                    else
                        fail[ch[v][i]]=ch[fail[v]][i];
                    q.push(ch[v][i]);
                }
            }
            for (int i=1;i<=lis[0];++i) st[lis[i]]|=st[fail[lis[i]]];
        }
        void dp(){
            int u;
            f[0][rt][0]=1;
            for (int i=0;i<m;++i){
                for (int j=rt;j<=tot;++j)
                    for (int stt=0;stt<=all;++stt){
                        if (f[i][j][stt]==0) continue;
                        for (int k=0;k<C;++k){
                            u=ch[j][k];
                            f[i+1][u][stt|st[u]]=plu(f[i+1][u][stt|st[u]],f[i][j][stt]);
                        }
                    }
            }
        }
        bool check(int which,int mid){
            int tot1=mid,tot2=mid+1,len=strlen(s[which]);
            while (tot1>=0&&tot2<len){
                if (s[which][tot1]==s[which][tot2]) return 0;
                --tot1; ++tot2;
            }
            return 1;
        }
        void calc_mid(){
            int len,now,c;
            for (int i=1;i<=n*2;++i){
                len=strlen(s[i]);
                now=rt;
                for (int j=0;j<len-1;++j){
                    c=s[i][j]-'0';
                    if (check(i,j)&&(j+1)*2>=len)
                        midst[ch[now][c]]|=St(i);
                    now=ch[now][c];
                }
            }
            for (int i=1;i<=lis[0];++i)
                midst[lis[i]]|=midst[fail[lis[i]]];
        }
        void solve(){
            build();
            dp();
            calc_mid();
            ans=0;
            for (int i=rt;i<=tot;++i){
                for (int stt=0;stt<=all;++stt){
                    if ((midst[i]|stt)==all)
                        ans=plu(ans,f[m][i][stt]);
                }
            }
            printf("%d
    ",ans);
        }
    }/*}}}*/
     
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("a.in","r",stdin);
    #endif
        scanf("%d%d",&n,&m);
        int len;
        Ac::init();
        all=1<<n; --all;
        for (int i=1;i<=n;++i){
            scanf("%s",s[i]);
            len=strlen(s[i]);
            for (int j=0;j<len;++j)
                s[n+i][len-1-j]='0'+((s[i][j]-'0')^1);
            Ac::insert(i);
            Ac::insert(n+i);
        }
        Ac::solve();
    }
    
  • 相关阅读:
    高盛、沃尔玛 题做出来还挂了的吐槽
    amazon师兄debrief
    到所有人家距离之和最短的中点 296. Best Meeting Point
    问问题没人回答的情况怎么办终于有解了
    找名人 277. Find the Celebrity
    数组生存游戏 289. Game of Life
    547. Number of Provinces 省份数量
    428. Serialize and Deserialize Nary Tree 序列化、反序列化n叉树
    alias别名简介和使用
    面试官:线程池执行过程中遇到异常会发生什么,怎样处理? Vincent
  • 原文地址:https://www.cnblogs.com/yoyoball/p/10073362.html
Copyright © 2011-2022 走看看