Codeforces Gym 101174 E. Passwords (AC 自动机 + DP)

来源:互联网 发布:linux属于应用软件吗 编辑:程序博客网 时间:2024/05/17 01:12

Problem

给定 N 个单词和整数 A ,B。合法的串 S 为长度在区间 [A, B] 内,仅有大小写字母和数字构成,至少有一个大写字母、一个小写字母和一个数字,且 N 个单词均无法在该串中找到匹配的子串。求 S 串的可行方案数(对结果 MOD 1000003)。

特殊情况:数字 0 1 3 5 7 可分别视作 o i e s t 进行单词串的匹配。同时匹配时忽略大小写。

限制条件

3 ≤ A ≤ B ≤ 20

0 ≤ N ≤ 50

1 ≤ length(Wi) ≤ 20

N 个单词均只有小写字母

解题思路

DP 处理所有合法情况的可行方案数。

  • 枚举每种合法的长度 S 。
  • 记录是否存在小写字母|大写字母|数字,通过状压记录
  • 判断该串的后续状态是否会产生单词串的匹配(使用 AC 自动机处理)。

dp[S][status][idx] 表示长度为 S ,大小写和数字状态为 status ,枚举的第 S 个字符在 AC 自动机中所属节点编号为 idx。

其后续状态有枚举 A-Z , a-z , 0-9 。其中 A-Z 由于忽略大小写应视为 a-z ,部分数字应直接处理成对应字母进行处理。

若有 idx 推导的状态加一个字符不会产生单词匹配,则 dp[S][status][idx] 对其后续产生贡献,否则贡献为 0 。

代码

#include<bits/stdc++.h>using namespace std;const int MOD = 1e6 + 3;const int maxn = 1010;const int CH = 128;int A, B, N, dp[22][8][1010];char buf[22];string str = "oi2e4s6t89";struct Trie {    int nxt[maxn][CH], fail[maxn], end[maxn];    int root, L;    void init() {        L = 0;        root = newnode();    }    int newnode() {        for(int i=0;i<CH;i++)   nxt[L][i] = -1;        end[L++] = 0;        return L-1;    }    void insert(char buf[], int idx) {        int p = root;        for(int i=0;buf[i];i++) {            if(nxt[p][buf[i]] == -1)                nxt[p][buf[i]] = newnode();            p = nxt[p][buf[i]];        }               end[p] = idx;    }    void build() {        queue<int> que;        fail[root] = root;        for(int i=1;i<CH;i++)            if(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;i<CH;i++)                if(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]);                }        }    }} ac;int findAndJugNxt(int nxt, char c) {    while(nxt != 0 && ac.nxt[nxt][c] == -1) nxt = ac.fail[nxt];    nxt = ac.nxt[nxt][c];    int tmpNxt = nxt;    while(tmpNxt != 0) {        if(ac.end[tmpNxt] != 0) return -1;        tmpNxt = ac.fail[tmpNxt];    }    return nxt;}int main(){    scanf("%d %d %d", &A, &B, &N);    ac.init();    for(int i=1;i<=N;i++)    {        scanf(" %s", buf);        ac.insert(buf, i);    }    ac.build();    dp[0][0][0] = 1;    for(int S=0;S<B;S++)    for(int status=0;status<8;status++)    for(int idx=0, nxt;idx<ac.L;idx++)    {        if(dp[S][status][idx] == 0) continue;        for(char c='a', tc;c<='z';c++)  //A-Z for ingore upper or lower         {            nxt = findAndJugNxt(idx, c);            if(nxt != -1)                (dp[S+1][status|1][nxt] += dp[S][status][idx]) %= MOD;        }        for(char c='a';c<='z';c++)        {            nxt = findAndJugNxt(idx, c);            if(nxt != -1)                (dp[S+1][status|2][nxt] += dp[S][status][idx]) %= MOD;        }        for(char c='0', tc;c<='9';c++)        {            tc = str[c-'0'];            nxt = findAndJugNxt(idx, tc);            if(nxt != -1)                (dp[S+1][status|4][nxt] += dp[S][status][idx]) %= MOD;        }    }    int ans = 0;    for(int S=A;S<=B;S++)    for(int idx=0;idx<ac.L;idx++)        (ans += dp[S][7][idx]) %= MOD;    printf("%d\n", ans);}