B 组都说看不懂……我也解释不清啊……只能写这么详细了

其实就是道板题

省流:f[i][s][j]表示字符串长度i,匹配情况s,ac自动机节点j

Problem Description

给定k个字符串以及长度为n的母串可选字母的集合,问母串要完整出现给定的k个字符串的方案数,答案模1000000007,字符仅包含小写字母。

Input

第一行两个整数n、k,表示字符串的长度和给定字符串的个数。

接下来k行每行一个字符串。

接下来一行1个整数m表示可选字母集合内元素个数。

接下来一行给出一个长为m的字符串,表示字母的集合(可能有重复)。

Output

一个整数ans,表示方案数。

Sample Input Copy

3 2
cr
rh
4
acrh

Sample Output Copy

1
【样例解释】
只有crh符合。

Data Constraint

30%的数据n<=10,m<=3。

60%的数据n<=40。

另有10%的数据k=0。

另有10%的数据m=1。

100%的数据n<=100,m<=10,k<=8,给定字符串长度<=30。


先想一个弱化版问题:给定 \(k\) 个模式串以及长度为 \(n\) 的母串可选字母的集合,对于一个母串,它的价值为出现模式串的个数。问所有合法母串的价值和。

怎么才能判定一个母串是否包含几个模式串?

我们可以想到 ac 自动机,考虑对模式串建 ac 自动机,定义 tail 为以一个节点为模式串结尾的个数。如果我们跑到了一个标记为 tail 的节点,说明我们的母串包含了这一个模式串。

模仿 ac 自动机的过程,用 \(f[i][j]\) 表示母串长度为 \(i\),走到了自动机上的节点 \(j\) 的价值和,然后枚举它的下一个字符 \(c\),明显有 \(f[i+1][son[j][c]]\gets f[i][j] + tail[son[j][c]]\)。


回到原问题,因为我们要求完整出现给定的 \(k\) 个模式串的方案数,所以我们状压模式串的出现情况为 \(s\)。

然后定义 tail 为以一个节点为模式串结尾的状压出现情况

同时,我们在 ac 自动机中有一个跳 fail 的步骤,这个步骤不是线性的,因为我们此时已经状压了,就可以直接在建树时把它传给子节点,有 \(tail[u]|=tail[fail[u]]\)。

所以我们设 \(f[i][s][j]\) 表示我们母串的长度为 \(i\),模式串的匹配状态为 \(s\)(状压后),当前母串跑到了 ac 自动机的节点 \(j\) 的方案数。

明显有 \(f[i+1][s|tail[son[j][c]]][son[j][c]]\gets f[i][s][j]\)。

#include <cstdio>
#include <queue>
using namespace std;
#define N 110
#define M 32
#define K 8
#define P 1000000007
int n, m, k, cnt, ans;
char s[M];
int can[M];
int f[2][1<<K][N*M];
int nxt[N*M][26];
int tail[N*M];
int fail[N*M];
bool vis[26];
void insert(int x) {
int p = 0;
for(int i = 1; s[i] != '\0'; i++) {
if(!nxt[p][s[i]-'a']) {
nxt[p][s[i]-'a'] = ++cnt;
}
p = nxt[p][s[i]-'a'];
}
tail[p] |= (1 << x);
}
void build() {
queue<int> q;
for(int i = 1; i <= m; i++)
if(nxt[0][can[i]]) q.push(nxt[0][can[i]]);
while(!q.empty()) {
int p = q.front();
q.pop();
tail[p] |= tail[fail[p]];
for(int i = 1; i <= m; i++) {
if(nxt[p][can[i]]) {
fail[nxt[p][can[i]]] = nxt[fail[p]][can[i]];
q.push(nxt[p][can[i]]);
} else {
nxt[p][can[i]] = nxt[fail[p]][can[i]];
}
}
}
}
int main() {
scanf("%d %d", &n, &k);
for(int i = 0; i < k; i++) {
scanf("%s", s+1);
insert(i);
}
scanf("%d", &m);
m = 0;
scanf("%s", s+1);
for(int i = 1; s[i] != '\0'; i++) {
if(!vis[s[i]-'a']) {
vis[s[i]-'a'] = 1;
can[++m] = s[i]-'a';
}
}
build();
f[0][0][0] = 1;
for(int i = 0; i < n; i++) {
for(int s = 0; s < (1<<k); s++) {
for(int p = 0; p <= cnt; p++) {
f[(i+1) % 2][s][p] = 0;
}
}
for(int s = 0; s < (1<<k); s++) {
for(int p = 0; p <= cnt; p++) {
if(!f[i%2][s][p]) continue;
for(int j = 1; j <= m; j++) {
int pp = nxt[p][can[j]];
(f[(i+1) % 2][s | tail[pp]][pp] += f[i%2][s][p]) %= P;
}
}
}
}
for(int i = 0; i <= cnt; i++) {
(ans += f[n%2][(1<<k)-1][i]) %= P;
}
printf("%d", ans);
}

【NOIP2013模拟联考8】匹配(match) 题解的更多相关文章

  1. JZOJ【NOIP2013模拟联考14】隐藏指令

    JZOJ[NOIP2013模拟联考14]隐藏指令 题目 Description 在d维欧几里得空间中,指令是一个长度为2N的串.串的每一个元素为d个正交基的方向及反方向之一.例如,d = 1时(数轴) ...

  2. JZOJ 3493. 【NOIP2013模拟联考13】三角形

    3493. [NOIP2013模拟联考13]三角形(triangle) (File IO): input:triangle.in output:triangle.out Time Limits: 10 ...

  3. JZOJ 3487. 【NOIP2013模拟联考11】剑与魔法(dragons)

    3487. [NOIP2013模拟联考11]剑与魔法(dragons) (Standard IO) Time Limits: 1000 ms  Memory Limits: 131072 KB  De ...

  4. JZOJ 3470. 【NOIP2013模拟联考8】最短路(path)

    470. [NOIP2013模拟联考8]最短路(path) (Standard IO) Time Limits: 1000 ms  Memory Limits: 262144 KB  Detailed ...

  5. JZOJ 3463. 【NOIP2013模拟联考5】军训

    3463. [NOIP2013模拟联考5]军训(training) (Standard IO) Time Limits: 2000 ms  Memory Limits: 262144 KB  Deta ...

  6. JZOJ 3462. 【NOIP2013模拟联考5】休息(rest)

    3462. [NOIP2013模拟联考5]休息(rest) (Standard IO) Time Limits: 1000 ms  Memory Limits: 262144 KB  Detailed ...

  7. JZOJ 3461. 【NOIP2013模拟联考5】小麦亩产一千八(kela)

    3461. [NOIP2013模拟联考5]小麦亩产一千八(kela) (Standard IO) Time Limits: 1000 ms  Memory Limits: 262144 KB  Det ...

  8. 【NOIP2013模拟联考7】OSU

    [NOIP2013模拟联考7]OSU 描述 Description osu 是一款群众喜闻乐见的休闲软件. 我们可以把osu的规则简化与改编成以下的样子: 一共有n次操作,每次操作只有成功与失败之分, ...

  9. [jzoj]3468.【NOIP2013模拟联考7】OSU!(osu)

    Link https://jzoj.net/senior/#main/show/3468 Description osu 是一款群众喜闻乐见的休闲软件. 我们可以把osu的规则简化与改编成以下的样子: ...

  10. 【NOIP2013模拟联考6】选课

    题目 你真的认为选课是那么容易的事吗?HYSBZ的ZY同志告诉你,原来选课也会让人产生一种想要回到火星的感觉.假设你的一周有n天,那么ZY编写的选课系统就会给你n堂课.但是该系统不允许在星期i和星期i ...

随机推荐

  1. echart 带表格

    "echarts": "^5.2.2", ChartSituation1 <template> <div> <EChartTemp ...

  2. linux下几种快速清空文件内容的方法

    [root@iamzken ~] : > filename [root@iamzken ~] > filename [root@iamzken ~] echo "" & ...

  3. ubuntu切换root到user

    目前知道: 从root用户切回user用户有三种方法: 1.su user (user是你自己安装时候的用户名) 2.直接输入exit 3.ctrl+D组合键

  4. Caffe源码编译,win10+vs2015+Ninja,C++接口测试(mnist),Python接口测试(mnist),(坑爹篇)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  5. Error: Command failed: C:\windows\system32\cmd.exe /s /c "./configure --disable-shared

    错误记录之: Error: Command failed: C:\windows\system32\cmd.exe /s /c "./configure --disable-shared 错 ...

  6. Android实际开发bug大总结

    目录介绍 1.1 java.lang.UnsatisfiedLinkError找不到so库异常 1.2 java.lang.IllegalStateException非法状态异常 1.3 androi ...

  7. Python glob库的用法

    一.匹配指定文件夹下的所有文件 files = glob.glob("C:\\Users\\liuchunlin2\\Desktop\\测试数据\\*") print(files) ...

  8. Oracle 几种行转列的方式 sum+decode sum+case when pivot

    原始数据: 方式一: select t_name, sum(decode(t_item, 'item1', t_num, 0)) item1, sum(decode(t_item, 'item2', ...

  9. docker下载mongodb镜像并启动容器

    1.查找mongodb相关镜像 docker search mongo 找到相关的镜像进行拉取,如果不指定版本,默认下载最新的mongoDB.建议自己先查找需要那个版本后在进行拉取,因为mongoDB ...

  10. DARTS:基于梯度下降的经典网络搜索方法,开启端到端的网络搜索 | ICLR 2019

    DARTS是很经典的NAS方法,它的出现打破了以往的离散的网络搜索模式,能够进行end-to-end的网络搜索.由于DARTS是基于梯度进行网络更新的,所以更新的方向比较准确,搜索时间相当于之前的方法 ...