「BZOJ 4502」串

题目描述

兔子们在玩字符串的游戏。首先,它们拿出了一个字符串集合 \(S\),然后它们定义一个字符串为“好”的,当且仅当它可以被分成非空的两段,其中每一段都是字符串集合 \(S\) 中某个字符串的前缀。比如对于字符串集合 \(\{ "abc","bca" \}\),字符串 \("abb"\),\("abab"\)是“好”的 \(("abb"="ab"+"b", abab="ab"+"ab")\) ,而字符串 \(“bc”\)不是“好”的。

兔子们想知道,一共有多少不同的“好”的字符串。

\(1 \leq N \leq 10000, 1 \leq |S| \leq 30\)

解题思路 :

观察发现,对于同一个串可能会有多种划分方式形成两个前缀拼接的形式,直接大力计算不方便处理重复的情况

此时不妨统计每一种答案串中最具有“特征”的那一种划分方式,在所有划分方式中,最小化第二个串的长度

也就是说,如果第一个串已经确定,第二个串的前缀与第一个串的公共部分全部划给第一个串

问题的一部分转化为一个 \(Trie\) 树上求 \(Borders\) 的问题,也就是 \(AC\) 自动机的 \(fail\) 指针,所以可以把问题规约到 \(AC\) 自动机上面

此时答案的形态有两种,拼接起来的串就是原串的一个前缀,或者是两个前缀拼接起来

考虑第一种情况,本质上是对于 \(AC\) 自动机中每一个 \(fail \neq root\) 的点,其到 \(root\) 的路径代表的前缀就是一个合法的答案

对于第二种情况,根据 \(AC\) 自动机的性质,匹配串和合法路径一一对应,所以问题可以转化为对合法路径计数

于是考虑在 \(AC\) 自动机上枚举第一个串,通过 \(dp\) 处理出每一个 \(Trie\) 树节点作为路径终点的答案,通过走树边和 \(fail\) 边来转移

设 \(f[i][j][k]\) 表示总长度 \(i\) 的串走到了节点 \(j\) ,枚举的第一个串的长度为 \(k\) 的答案

转移就直接走 \(Trie\) 图的边转移,但要保证任意时刻拼接起来的串长要能够等于 \(i\) ,也就是 \(dep(j) + k > i\)

但是这样的复杂度是 \(O(n\times60^2\times26)\) 的,时间复杂度不能够接受,考虑对状态进行简化

观察发现,将不等式稍微加变换就是 \(dep(j) > i - k\) ,那么只需要记录 \(f[i][j]\) 表示第二个串长为 \(i\) ,当前到达了节点 \(j\) 的方案数,现在复杂度是 \(O(n \times 26 \times 60)\)

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
#define int ll
#define par pair<int, int>
#define mp make_pair
#define fi first
#define se second const int N = 3000005;
char s[N];
int f[65][N], n; struct ACautomaton{
queue<int> q; int ch[N][26], dep[N], nxt[N][26], fail[N], size;
inline ACautomaton(){
for(int i = 0; i < 26; i++) nxt[0][i] = 1; size = 1;
}
inline int newnode(int x){ return dep[++size] = x, size; }
inline void ins(char *s){
int p = 1, len = strlen(s);
for(int i = 0; i < len; i++){
int c = s[i] - 'a';
if(!ch[p][c]) ch[p][c] = nxt[p][c] = newnode(i + 1);
p = ch[p][c];
}
}
inline void build(){
for(q.push(1); !q.empty(); ){
int u = q.front(); q.pop();
for(int i = 0; i < 26; i++){
int v = nxt[u][i];
if(!v) nxt[u][i] = nxt[fail[u]][i];
else fail[v] = nxt[fail[u]][i], q.push(v);
}
}
}
inline void solve(){
int ans = 0;
for(int i = 2; i <= size; i++) ans += (fail[i] != 1);
for(int i = 1; i <= size; i++)
for(int j = 0; j < 26; j++)
if(!ch[i][j] && nxt[i][j] != 1) f[1][nxt[i][j]]++;
for(int i = 1; i <= 60; i++)
for(int j = 1; j <= size; j++) if(f[i][j]){
for(int c = 0; c < 26; c++)
if(dep[nxt[j][c]] > i) f[i+1][nxt[j][c]] += f[i][j];
}
for(int i = 1; i <= 60; i++)
for(int j = 1; j <= size; j++) ans += f[i][j];
cout << ans;
}
}van; signed main(){
read(n);
for(int i = 1; i <= n; i++) scanf("%s", s), van.ins(s);
van.build(), van.solve();
return 0;
}

「BZOJ 4502」串的更多相关文章

  1. 「BZOJ 2534」 L - gap字符串

    「BZOJ 2534」 L - gap字符串 题目描述 有一种形如 \(uv u\) 形式的字符串,其中 \(u\) 是非空字符串,且 \(v\) 的长度正好为 \(L\), 那么称这个字符串为 \( ...

  2. 「BZOJ 4228」Tibbar的后花园

    「BZOJ 4228」Tibbar的后花园 Please contact lydsy2012@163.com! 警告 解题思路 可以证明最终的图中所有点的度数都 \(< 3\) ,且不存在环长是 ...

  3. 「BZOJ 3645」小朋友与二叉树

    「BZOJ 3645」小朋友与二叉树 解题思路 令 \(G(x)\) 为关于可选大小集合的生成函数,即 \[ G(x)=\sum[i\in c ] x^i \] 令 \(F(x)\) 第 \(n\) ...

  4. 「BZOJ 4289」 PA2012 Tax

    「BZOJ 4289」 PA2012 Tax 题目描述 给出一个 \(N\) 个点 \(M\) 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 \(1\) 到点 \( ...

  5. 「BZOJ 2956」模积和

    「BZOJ 2956」模积和 令 \(l=\min(n,m)\).这个 \(i\neq j\) 非常不优雅,所以我们考虑分开计算,即: \[\begin{aligned} &\sum_{i=1 ...

  6. Solution -「BZOJ 3812」主旋律

    \(\mathcal{Description}\)   Link.   给定含 \(n\) 个点 \(m\) 条边的简单有向图 \(G=(V,E)\),求 \(H=(V,E'\subseteq E)\ ...

  7. 「BZOJ 1001」狼抓兔子

    题目链接 luogu bzoj \(Solution\) 这个貌似没有什么好讲的吧,直接按照这个给的图建图就好了啊,没有什么脑子,但是几点要注意的: 建双向边啊. 要这么写,中间还要写一个\(whil ...

  8. 「BZOJ 5188」「Usaco2018 Jan」MooTube

    题目链接 luogu bzoj \(Describe\) 有一个\(n\)个节点的树,边有权值,定义两个节点之间的距离为两点之间的路径上的最小边权 给你\(Q\)个询问,问你与点\(v\)的距离大于等 ...

  9. 「BZOJ 2342」「SHOI 2011」双倍回文「Manacher」

    题意 记\(s_R\)为\(s\)翻转后的串,求一个串最长的形如\(ss_Rss_R\)的子串长度 题解 这有一个复杂度明显\(O(n)\)的做法,思路来自网上某篇博客 一个双倍回文串肯定当且仅当本身 ...

随机推荐

  1. 【CodeForces】671 C. Ultimate Weirdness of an Array

    [题目]C. Ultimate Weirdness of an Array [题意]给定长度为n的正整数序列,定义一个序列的价值为max(gcd(ai,aj)),1<=i<j<=n, ...

  2. json格式的一些常用操作方法

    package com.liveyc.restfull.until; import java.util.HashMap; import java.util.Iterator; import java. ...

  3. scrapy 爬虫踩过的坑(I)

    问题1:正则表达式没问题,但是爬虫进不了item方法 分析: 1. 可能是下载不到list 页面的内容.可以用 scrapy shell url 进行测试 2. 可能是allowed_domains ...

  4. Perl6 必应抓取(1):测试版代码

    一个相当丑漏的代码, 以后有时间再优化了. 默认所有查找都是15页, 如果结果没有15页这么多估计会有重复.速度还是很快的. sub MAIN() { my $fp = open 'bin_resul ...

  5. perl6正则 1: ~~ , //, m//, rx//

    ~~ perl6 中, 要匹配一个正则, 使用 ~~ 智能匹配符. > so 'abcde' ~~ /a.c/ True > so 'abcde' ~~ /a.d/ False > ...

  6. Mac nginx 配置

    nginx 安装: 在苹果系统下如果要安装nginx,首先要安装brew.安装brew可以查看网站:https://brew.sh: 一条命令即可搞定:/usr/bin/ruby -e "$ ...

  7. javascript 之数据类型--01

    写在前面 国庆整理资料时,发现刚开始入门前端时学习JS 的资料,打算以一个基础入门博客记录下来,有不写不对的多多指教: 先推荐些书籍给需要的童鞋 <JavaScript 高级程序设计.pdf&g ...

  8. C++ 模版的优点和缺点

    优点: 1. 灵活性, 可重用性和可扩展性; 2. 可以大大减少开发时间,模板可以把用同一个算法去适用于不同类型数据,在编译时确定具体的数据类型; 3. 模版模拟多态要比C++类继承实现多态效率要高, ...

  9. java版云笔记(二)

    云笔记 基本的环境搭建好了,今天做些什么呢,第一是链接数据库(即搭建Spring-Batistas环境),第二是登录预注册. 注:这个项目的sql文件,需求文档,需要的html文件,jar包都可以去下 ...

  10. CGI、FastCGI和php-fpm的概念和区别

    CGI是HTTP Server和一个独立的进程之间的协议,把HTTP Request的Header设置成进程的环境变量,HTTP Request的正文设置成进程的标准输入,而进程的标准输出就是HTTP ...