字符串

Description

  數據範圍:\(n<=6,|s_i|<=100,m<=500\)node

  

Solution

​  場上不會在ac自動機上面跑dp的我大概失去了智商==果真字符串這塊仍是有點薄弱啊ios

  首先想一個比較好的計算方式:比較明白的一點是若是前\(m\)位肯定了,後\(m\)位天然也就肯定了,咱們將知足條件的串分紅三大類:c++

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

(2)匹配串都在後\(m\)位(後半段)spa

(3)匹配串跨\(m\)這個位置debug

​  第三類又能夠再分兩類:跨\(m\)這個位置的串在前半段的長度比較大、在後半段的長度比較大code

​   

  首先考慮前兩類怎麼計算:其實只要把正串和翻轉以後再\(01\)反轉的串都丟到ac自動機裏面而後跑dp就行了blog

  看到這個\(n\)這麼小,大概差很少就是用來狀壓的了吧,因而粗暴地令\(f[i][j][k]\)表示肯定了前\(i\)位,當前在\(j\)這個節點,當前已經包含的串狀態爲\(k\),而後直接\(O(m*\)自動機節點數\(*2^n)\)暴力dp就行了ip

  具體一點就是對於每一個節點記錄一個\(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("\n");
    }
    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\n",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();
}
相關文章
相關標籤/搜索