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

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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值