BZOJ1559 [JSOI2009]密码 【AC自动机 + 状压dp】

题目链接

BZOJ1559

题解

考虑到这是一个包含子串的问题,而且子串非常少,我们考虑\(AC\)自动机上的状压\(dp\)
\(f[i][j][s]\)表示长度为\(i\)的串,匹配到了\(AC\)自动机\(j\)号节点,且已匹配集合为\(s\)的方案数
直接在\(AC\)自动机上转移即可
但是为了防止使用\(last\)指针之类的,计算匹配的串,我们先将原串的集合去重和去包含关系

方案怎么办?
考虑到\(ans \le 42\),一定是刚好若干个原串以最长前后缀相同的方式相接
因为如果不是以最长前后缀的方式相接,那么一定存在更小的方案,这个时候就会多出若干空位置可以随便填
一个位置可以填\(26\)种字母,而且可以放在前缀或者后缀,所以有一个空位置就有\(52\)种方案,大于\(42\)
所以可以放心\(O(n!)\)枚举排列相接

\(1A\)了很开心

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 105,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int ch[maxn][26],fail[maxn],tag[maxn],cnt,len[12];
char s[12][12];
int isin[maxn];
int same[12][12];
LL f[26][105][1 << 10];
void ins(int p){
    int u = 0,id;
    for (int i = 1; i <= len[p]; i++){
        id = s[p][i] - 'a';
        u = ch[u][id] ? ch[u][id] : (ch[u][id] = ++cnt);
    }
    tag[u] |= (1 << p - 1);
}
void getf(){
    queue<int> q;
    for (int i = 0; i < 26; i++) if (ch[0][i]) q.push(ch[0][i]);
    int u,v;
    while (!q.empty()){
        u = q.front(); q.pop();
        for (int i = 0; i < 26; i++){
            v = ch[u][i];
            if (!v){
                ch[u][i] = ch[fail[u]][i];
                continue;
            }
            fail[v] = ch[fail[u]][i];
            q.push(v);
        }
    }
}
int L,n;
int check(int u,int v){
    if (len[u] > len[v]) return 0;
    if (len[u] == len[v]){
        for (int i = 1; i <= len[u]; i++)
            if (s[u][i] != s[v][i])
                return 0;
        return 1;
    }
    for (int i = 1; i <= len[v] - len[u] + 1; i++){
        int flag = true;
        for (int j = 1; j <= len[u]; j++){
            if (s[u][j] != s[v][i + j - 1]){
                flag = false;
                break;
            }
        }
        if (flag) return 2;
    }
    return 0;
}
int cal(int u,int v){
    for (int i = max(1,len[u] - len[v] + 1); i <= len[u]; i++){
        int flag = true;
        for (int j = 1; i + j - 1 <= len[u]; j++)
            if (s[u][i + j - 1] != s[v][j]){
                flag = false;
                break;
            }
        if (flag) return len[u] - i + 1;
    }
    return 0;
}
int vis[maxn],a[maxn],ansi,Ltot;
struct node{
    char s[30];
    int len;
}ans[50];
inline bool operator <(const node& a,const node& b){
    return strcmp(a.s + 1,b.s + 1) < 0;
}
void Check(){
    int sum = Ltot;
    for (int i = 1; i < n; i++)
        sum -= same[a[i]][a[i + 1]];
    if (sum == L){
        ansi++;
        for (int i = 1; i <= n; i++){
            int u = a[i];
            for (int j = same[a[i - 1]][u] + 1; j <= len[u]; j++){
                ans[ansi].s[++ans[ansi].len] = s[u][j];
            }
        }
    }
}
void dfs(int u){
    if (u > n){
        Check();
        return;
    }
    for (int i = 1; i <= n; i++)
        if (!vis[i]){
            vis[i] = true;
            a[u] = i;
            dfs(u + 1);
            vis[i] = false;
        }
}
void work(){
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i != j) same[i][j] = cal(i,j);
    for (int i = 1; i <= n; i++) Ltot += len[i];
    dfs(1);
    sort(ans + 1,ans + 1 + ansi);
    for (int i = 1; i <= ansi; i++)
        printf("%s\n",ans[i].s + 1);
}
int main(){
    L = read(); n = read();
    for (int i = 1; i <= n; i++){
        scanf("%s",s[i] + 1);
        len[i] = strlen(s[i] + 1);
    }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i != j ){
                int t = check(i,j);
                if (t == 2 || (t == 1 && i > j))
                    isin[i] = true;
            }
    int tot = 0;
    for (int i = 1; i <= n; i++)
        if (!isin[i]){
            strcpy(s[++tot] + 1,s[i] + 1);
            len[tot] = len[i];
            ins(tot);
        }
    n = tot;
    getf();
    int maxv = (1 << n) - 1,u;
    f[0][0][0] = 1;
    for (int i = 0; i < L; i++){
        for (int j = 0; j <= cnt; j++){
            for (int s = 0; s <= maxv; s++){
                if (!f[i][j][s]) continue;
                for (int k = 0; k < 26; k++){
                    u = ch[j][k];
                    f[i + 1][u][s | tag[u]] += f[i][j][s];
                }
            }
        }
    }
    LL ans = 0;
    for (int i = 0; i <= cnt; i++)
        ans += f[L][i][maxv];
    printf("%lld\n",ans);
    if (ans <= 42) work();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Mychael/p/9074401.html